Late Night Confessions — Building a Website Using Rust, Rocket, Diesel, and Askama — Part 3
Johnny Tordgeman

This is part 3 in a three-part series. Read part 1 here and part 2 here.
With a working API layer under our belt, it’s time to put in the final piece of the puzzle: the presentation layer. This layer consists of static resources (HTML, CSS, JavaScript) and API calls to display and add confessions.
✏️ As this tutorial is not frontend focused, I wanted to keep things simple and use vanilla JavaScript, HTML and CSS. I will also not go into too much detail with why or how these are implemented, as we are not really here for do frontend stuff are we?
Working With Templates
When we serve index.html back to the user, we want it to have a confession already so the user won’t load the page and then wait for the first API call to return the confession. That basically means that our application needs to parse the index.html static file, find the right place to inject a confession, and then inject the content in the correct place after fetching a random confession from the database. Now that might work if you have one item to inject, but what if you have two or ten?
That’s where templates come into play. Templates allow us to have a static HTML that contains placeholders (aka variables) that the templating engine will replace with the content of our choice. In addition, templates allow us to use tags to control the template logic if
statements, for example). However, we will not be dealing with those in this tutorial.
For Late Night Confessions, I chose Askama as the templating engine because of its easy integration and simple syntax. Let’s create a template for our index.html file (trust me, it’s easier than you think):
- In the project root folder, create a new folder called templates.
- Inside the templates folder, create a new file called index.html with the following content:
<span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Late Night Confessions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;1,400&display=swap<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://cdn.jsdelivr.net/npm/cirrus-ui@0.6.0/dist/cirrus.min.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/static/styles.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>./static/confessions.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stars<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>twinkling<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clouds<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>avatar avatar--xlarge<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://images.unsplash.com/photo-1483706600674-e0c87d3fe85b?dpr=1&auto=format&fit=crop&w=256&h=256&q=60&cs=tinysrgb&crop=fill&bg=fff<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Late Night Confessions<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>confessionView<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>card-container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>card-image<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>confessionCardImage<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>{{confession}}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>timeBar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>progress<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>progress-value<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>addView<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#confession-modal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>outline btn-primary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Add Yours<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>♥️<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> {{total_confessions}} confessions on record!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modal modal-animated--zoom-in<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>confession-modal<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modal-overlay<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modal-content confessionAdd<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>document<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modal-header<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#main<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>u-pull-right<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>xBtn<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>X<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modal-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Confess your <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>heart<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>♥️<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> out<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modal-body<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>textarea</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>confessionText<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Enter your confession<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>textarea</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>modal-footer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>form-section u-text-right<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn btn-small u-inline-block<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cancel<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn-primary btn btn-small<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btnSubmit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Post<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span>
The vast majority of this file is plain old HTML syntax, take a glance at lines 32 and 47— These are Askama variables:
confession
(line 32) — This variable will be replaced by an actual confession from the database.total_confessions
(line 47) — This variable will be replaced by the total number of confessions in our database.
And that’s basically that for making our HTML an Askama template. Let’s quickly add the styles.css and confessions.js files to the site/static folder and get back to Rust to integrate Askama:
styles.css:
<span class="token selector">*</span> <span class="token punctuation">{</span>
<span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">body</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.main</span> <span class="token punctuation">{</span>
<span class="token property">z-index</span><span class="token punctuation">:</span> 5<span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token property">padding-top</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">;</span>
<span class="token property">margin</span><span class="token punctuation">:</span> 0 auto<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.title h1</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> 36px<span class="token punctuation">;</span>
<span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">'Lato'</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span>
<span class="token property">margin-top</span><span class="token punctuation">:</span> 0.5em<span class="token punctuation">;</span>
<span class="token property">margin-left</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token property">margin-right</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token property">color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">-webkit-linear-gradient</span><span class="token punctuation">(</span>#eee<span class="token punctuation">,</span> #333<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">-webkit-background-clip</span><span class="token punctuation">:</span> text<span class="token punctuation">;</span>
<span class="token property">background-clip</span><span class="token punctuation">:</span> text<span class="token punctuation">;</span>
<span class="token property">-webkit-text-fill-color</span><span class="token punctuation">:</span> transparent<span class="token punctuation">;</span>
<span class="token property">text-shadow</span><span class="token punctuation">:</span> 6px 6px 0px <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token property">z-index</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@keyframes</span> move-clouds-back</span> <span class="token punctuation">{</span>
<span class="token selector">from</span> <span class="token punctuation">{</span>
<span class="token property">background-position</span><span class="token punctuation">:</span> 0 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">to</span> <span class="token punctuation">{</span>
<span class="token property">background-position</span><span class="token punctuation">:</span> 10000px 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@keyframes</span> move-twinkle-back</span> <span class="token punctuation">{</span>
<span class="token selector">from</span> <span class="token punctuation">{</span>
<span class="token property">background-position</span><span class="token punctuation">:</span> 0 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">to</span> <span class="token punctuation">{</span>
<span class="token property">background-position</span><span class="token punctuation">:</span> -10000px 5000px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token selector">.stars,
.twinkling,
.clouds</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.stars</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> #000 <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>/assets/stars.png<span class="token punctuation">)</span></span> repeat top center<span class="token punctuation">;</span>
<span class="token property">z-index</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.twinkling</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> transparent <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>/assets/twinkling.png<span class="token punctuation">)</span></span> repeat top center<span class="token punctuation">;</span>
<span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> move-twinkle-back 200s linear infinite<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.clouds</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> transparent <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>/assets/clouds.png<span class="token punctuation">)</span></span> repeat top center<span class="token punctuation">;</span>
<span class="token property">z-index</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span>
<span class="token property">opacity</span><span class="token punctuation">:</span> 0.4<span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> move-clouds-back 200s linear infinite<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.avatar</span> <span class="token punctuation">{</span>
<span class="token property">border</span><span class="token punctuation">:</span> 2px solid #333<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.progress</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>255<span class="token punctuation">,</span> 255<span class="token punctuation">,</span> 255<span class="token punctuation">,</span> 0.1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">justify-content</span><span class="token punctuation">:</span> flex-start<span class="token punctuation">;</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span>
<span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 0 5px<span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.progress-value</span> <span class="token punctuation">{</span>
<span class="token property">animation</span><span class="token punctuation">:</span> load 30s normal forwards<span class="token punctuation">;</span>
<span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 10px 40px -10px #fff<span class="token punctuation">;</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> #c21b2b<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@keyframes</span> load</span> <span class="token punctuation">{</span>
<span class="token selector">0%</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">100%</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 0%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token selector">.confessionAdd</span> <span class="token punctuation">{</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> 450px <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.heart</span> <span class="token punctuation">{</span>
<span class="token property">color</span><span class="token punctuation">:</span> #c21b2b<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.addView</span> <span class="token punctuation">{</span>
<span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token property">padding-top</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token property">padding-bottom</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.modal.shown .modal-overlay,
.modal:target .modal-overlay</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>54<span class="token punctuation">,</span> 54<span class="token punctuation">,</span> 54<span class="token punctuation">,</span> 0.8<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.modal-header span.xBtn</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> 1.4rem<span class="token punctuation">;</span>
<span class="token property">color</span><span class="token punctuation">:</span> #374054<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.emptyArea</span> <span class="token punctuation">{</span>
<span class="token property">border</span><span class="token punctuation">:</span> 2px solid #c21b2b<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">footer</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">padding-bottom</span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">footer span</span> <span class="token punctuation">{</span>
<span class="token property">color</span><span class="token punctuation">:</span> #f03d4d<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
confessions.js:
<span class="token keyword">const</span> <span class="token constant">BASE_URL</span> <span class="token operator">=</span> <span class="token string">'http://localhost:8000'</span><span class="token punctuation">;</span>
<span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">refreshCard</span><span class="token punctuation">(</span><span class="token parameter">cardContent<span class="token punctuation">,</span> cardImage<span class="token punctuation">,</span> progressBar</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> resp <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token constant">BASE_URL</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/api/confession</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
headers<span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token string">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'application/json'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> respJson <span class="token operator">=</span> <span class="token keyword">await</span> resp<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
cardImage<span class="token punctuation">.</span>style<span class="token punctuation">.</span>backgroundImage <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">url('https://source.unsplash.com/featured/600x400/?city,night&seed=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">')</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
cardContent<span class="token punctuation">.</span>innerText <span class="token operator">=</span> respJson<span class="token punctuation">.</span>confession<span class="token punctuation">;</span>
progressBar<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">'progress-value'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">void</span> progressBar<span class="token punctuation">.</span>offsetWidth<span class="token punctuation">;</span>
progressBar<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">'progress-value'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'load'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> progressBar <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.progress-value'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> cardImage <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'confessionCardImage'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> cardContent <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.content p'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> btnSubmit <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'btnSubmit'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> footerNote <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'footer span'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>nextSibling<span class="token punctuation">;</span>
cardImage<span class="token punctuation">.</span>style<span class="token punctuation">.</span>backgroundImage <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">url('https://source.unsplash.com/featured/600x400/?city,night&seed=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">')</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
btnSubmit<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> confessionArea <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'confessionText'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
confessionArea<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">'emptyArea'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>confessionArea<span class="token punctuation">.</span>value<span class="token punctuation">.</span>length <span class="token operator"><=</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
confessionArea<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">'emptyArea'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
btnSubmit<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'disabled'</span><span class="token punctuation">,</span> <span class="token string">'disabled'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> resp <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token constant">BASE_URL</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/api/confession</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
method<span class="token operator">:</span> <span class="token string">'POST'</span><span class="token punctuation">,</span>
headers<span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token string">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'application/json'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
body<span class="token operator">:</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span> content<span class="token operator">:</span> confessionArea<span class="token punctuation">.</span>value <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>resp<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token number">201</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'Your confession is safe with us!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> currentNumber <span class="token operator">=</span> footerNote<span class="token punctuation">.</span>textContent<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">.+d</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
footerNote<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">parseInt</span><span class="token punctuation">(</span>currentNumber<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> confessions on record!</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'Error saving your confession. Try again later.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>href <span class="token operator">=</span> <span class="token string">'/#main'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
progressBar<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'animationend'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">await</span> <span class="token function">refreshCard</span><span class="token punctuation">(</span>cardContent<span class="token punctuation">,</span> cardImage<span class="token punctuation">,</span> progressBar<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
A few points of interest in our JavaScript:
- The
refreshCard
function makes a call to our GET API to get a new random confession and update the UI. - The event listener for click validates the text area (so it is not empty, somewhat naive, I know, but ♂️), and then if all is well, makes a call to our POST API with the new confession. If everything checks out (line 43), we alert the user everything is fine and update the footer note with the updated saved confessions number.
- We listen to the
animationend
event to update the displayed confession once the timer runs off (lines 53–55).
Working With Askama
1. Back in our Rust project, let’s add Askama to the Cargo.toml file:
<span class="token punctuation">[</span><span class="token table class-name">dependencies</span><span class="token punctuation">]</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token key property">askama</span> <span class="token punctuation">=</span> <span class="token string">"0.10.5"</span>
2. As mentioned earlier, Askama works with templates, so the first thing we need to do is tell Askama where our template is and which variables it’s going to have:
use askama::Template;
<span class="token comment">#[derive(Template)]</span>
<span class="token comment">#[template(path = "index.html")]</span>
struct HomepageTemplate <span class="token punctuation">{</span>
confession: String<span class="token punctuation">,</span>
total_confessions: i64<span class="token punctuation">,</span>
<span class="token punctuation">}</span>
So what do we have here?
- Line 3: We derive the Template trait, which is the main trait Askama uses. It includes template related methods such as render.
- Line 4: We use the template attribute to specify the path to our template. The path is relative to the templates folder in the project root folder. Other than path you can also specify a source (directly set the template, without a template file) or enable debug using the print sub-attribute. Read more about the different uses of the template attribute on the official docs.
- Lines 5–8: We define the fields the struct will hold, which are identical to the variables we defined in the template itself.
3. We now have two functions that need to get a random confession from the database: get_confession
(the handler for the GET API) and root
(the handler that renders index.html). To avoid code duplication, let’s extract get_confession
’s confession fetching logic to a new function, get_random_confession
:
<span class="token keyword">fn</span> <span class="token function-definition function">get_random_confession</span><span class="token punctuation">(</span>conn<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token class-name">PgConnection</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token class-name">Result</span><span class="token operator"><</span><span class="token class-name">Confession</span><span class="token punctuation">,</span> <span class="token namespace">diesel<span class="token punctuation">::</span>result<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token namespace">schema<span class="token punctuation">::</span>confessions<span class="token punctuation">::</span></span>table
<span class="token punctuation">.</span><span class="token function">order</span><span class="token punctuation">(</span><span class="token constant">RANDOM</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">limit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">first</span><span class="token punctuation">::</span><span class="token operator"><</span><span class="token class-name">Confession</span><span class="token operator">></span><span class="token punctuation">(</span>conn<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
Notice that our new function takes a conn
variable of type &PgConnection
as argument and return a Result
of either a Confession
(if successful) or diesel’s error type (if there was a database-related error).
4. With the get_random_confession
function in place, let’s update the get_confession
handler to use the new function as its closure:
<span class="token attribute attr-name">#[get(<span class="token string">"/confession"</span>, format = <span class="token string">"json"</span>)]</span>
<span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">get_confession</span><span class="token punctuation">(</span>conn<span class="token punctuation">:</span> <span class="token class-name">DBPool</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token class-name">Result</span><span class="token operator"><</span><span class="token class-name">Json</span><span class="token operator"><</span><span class="token class-name">Confession</span><span class="token operator">></span><span class="token punctuation">,</span> <span class="token class-name">CustomError</span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> confession<span class="token punctuation">:</span> <span class="token class-name">Confession</span> <span class="token operator">=</span> conn<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>c<span class="token closure-punctuation punctuation">|</span></span> <span class="token function">get_random_confession</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">await</span><span class="token operator">?</span><span class="token punctuation">;</span>
<span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token class-name">Json</span><span class="token punctuation">(</span>confession<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
5. Lastly, let’s update the root
handler to render the template using Askama and set it as the response:
<span class="token attribute attr-name">#[get(<span class="token string">"/"</span>)]</span>
<span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">root</span><span class="token punctuation">(</span>conn<span class="token punctuation">:</span> <span class="token class-name">DBPool</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token class-name">Result</span><span class="token operator"><</span><span class="token namespace">content<span class="token punctuation">::</span></span><span class="token class-name">Html</span><span class="token operator"><</span><span class="token class-name">String</span><span class="token operator">></span><span class="token punctuation">,</span> <span class="token class-name">CustomError</span><span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> confession_from_db<span class="token punctuation">:</span> <span class="token class-name">Confession</span> <span class="token operator">=</span> conn<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>c<span class="token closure-punctuation punctuation">|</span></span> <span class="token function">get_random_confession</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">await</span><span class="token operator">?</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> confession_count<span class="token punctuation">:</span><span class="token keyword">i64</span> <span class="token operator">=</span> conn<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>c<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
<span class="token namespace">schema<span class="token punctuation">::</span>confessions<span class="token punctuation">::</span></span>table<span class="token punctuation">.</span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get_result</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">await</span><span class="token operator">?</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> template <span class="token operator">=</span> <span class="token class-name">HomepageTemplate</span> <span class="token punctuation">{</span>
confession<span class="token punctuation">:</span> confession_from_db<span class="token punctuation">.</span>confession<span class="token punctuation">,</span>
total_confessions<span class="token punctuation">:</span> confession_count<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> response <span class="token operator">=</span> <span class="token namespace">content<span class="token punctuation">::</span></span><span class="token class-name">Html</span><span class="token punctuation">(</span>template<span class="token punctuation">.</span><span class="token function">to_string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Ok</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
So what do we have here?
- Line 2: We changed the return type to
Html<String>
for success and ourCustomError
in case of an error. - Line 3: We get a random confession from the database using the new
get_random_confession
function, just like we are doing for the API call to /api/confession. - Lines 4–6: We query Postgres for the number of confessions we have saved on it.
- Lines 8–11: We initiate the
HomepageTemplate
struct (a.k.a our template definition) with the data from Postgres. - Lines 13–14: This is where the magic happens. Askama turns our template into an HTML string (line 13), which we return as the handler response (line 14).
And that’s basically it! Go ahead and run cargo run
and browse to localhost:8000. You should get something similar to the following:
Summary
Our site creation journey has come to its end, but take a look back at everything you achieved! You now have a web app that is able to handle static files and API requests, uses a Postgres instance for persisting data and takes advantage of templates for quicker and easier HTML manipulation.
I would love to read your comments on this mini-post series over on Twitter, and please join me on my next Rust post series: “Let’s Build a Fitness Tracking Webapp With Rust and Yew”.
Credit Where Credit’s Due
- Thank you, Carla Notarobot, for the starry night CSS animation background!
- Thank you, Traf, for the progress bar CSS animation!