<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Marc Stammerjohann</title>
    <description>The latest articles on Forem by Marc Stammerjohann (@marcjulian).</description>
    <link>https://forem.com/marcjulian</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F352438%2F88b5b75d-b183-4fb9-a1d5-3b634622dba9.jpeg</url>
      <title>Forem: Marc Stammerjohann</title>
      <link>https://forem.com/marcjulian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/marcjulian"/>
    <language>en</language>
    <item>
      <title>Send Emails with NestJS</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Thu, 18 Mar 2021 10:56:54 +0000</pubDate>
      <link>https://forem.com/notiz_dev/send-emails-with-nestjs-14im</link>
      <guid>https://forem.com/notiz_dev/send-emails-with-nestjs-14im</guid>
      <description>&lt;p&gt;This post gets you up and running with everything you need to know about sending Emails using &lt;a href="https://github.com/nest-modules/mailer" rel="noopener noreferrer"&gt;nest-modules/mailer&lt;/a&gt; in your &lt;a href="https://github.com/nestjs/nest" rel="noopener noreferrer"&gt;NestJS&lt;/a&gt; backend. 👇&lt;/p&gt;

&lt;p&gt;📧 Sending emails using &lt;a href="https://nodemailer.com/about/" rel="noopener noreferrer"&gt;Nodemailer&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🧩 Creating email templates with &lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;handlebars&lt;/a&gt; (alternatives: pug or ejs)&lt;br&gt;&lt;br&gt;
⚙️ Configure smtp via &lt;code&gt;.env&lt;/code&gt; file&lt;/p&gt;
&lt;h2&gt;
  
  
  Install Dependencies
&lt;/h2&gt;

&lt;p&gt;Add the &lt;code&gt;@nestjs-modules/mailer&lt;/code&gt; and the peer dependency &lt;code&gt;nodemailer&lt;/code&gt; to your Nest application. Choose one of the supported template engines for creating your email templates: &lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;handlebars&lt;/a&gt;, &lt;a href="https://pugjs.org/api/getting-started.html" rel="noopener noreferrer"&gt;pug&lt;/a&gt; or &lt;a href="https://ejs.co/" rel="noopener noreferrer"&gt;ejs&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; @nestjs-modules/mailer nodemailer

&lt;span class="c"&gt;# pick one template adapter and install&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; handlebars
&lt;span class="c"&gt;# or&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; pug
&lt;span class="c"&gt;# or&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; ejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this guide, you are creating email templates using handlebars.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; @nestjs-modules/mailer nodemailer handlebars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mail Module
&lt;/h2&gt;

&lt;p&gt;Let's begin with creating a &lt;code&gt;mail&lt;/code&gt; module and service via the Nest CLI and followed by creating a &lt;code&gt;templates&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nest g module mail
nest g service mail

&lt;span class="nb"&gt;mkdir &lt;/span&gt;src/mail/templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the &lt;code&gt;MailerModule&lt;/code&gt; into your &lt;code&gt;MailModule&lt;/code&gt; and configure your mail server transport via &lt;code&gt;smtp&lt;/code&gt;. Provide a default &lt;code&gt;from&lt;/code&gt; email address to consistently use the same mail throughout your application. No worries, you can always override the default whenever necessary. Last step, configure the templates folder and the adapter in this case &lt;code&gt;HandlebarsAdapter&lt;/code&gt;. Find out more about the other template adapters in the &lt;a href="https://nest-modules.github.io/mailer/docs/mailer#configuration" rel="noopener noreferrer"&gt;Mailer documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailerModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs-modules/mailer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HandlebarsAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs-modules/mailer/dist/adapters/handlebars.adapter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./mail.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;MailerModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// transport: 'smtps://user@example.com:topsecret@smtp.example.com',&lt;/span&gt;
      &lt;span class="c1"&gt;// or&lt;/span&gt;
      &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smtp.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;topsecret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"No Reply" &amp;lt;noreply@example.com&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;templates&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HandlebarsAdapter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// or new PugAdapter() or new EjsAdapter()&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MailService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MailService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// 👈 export for DI&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MailModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export the &lt;code&gt;MailService&lt;/code&gt; to provide it via Dependency Injection (DI) for your controllers, resolvers and services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handlebars Mail Template
&lt;/h2&gt;

&lt;p&gt;Create your first email template &lt;code&gt;confirmation.hbs&lt;/code&gt; in the &lt;code&gt;src/mail/templates&lt;/code&gt; folder. Add the following simple template for a user confirmation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hey {{ name }},&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Please click below to confirm your email&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ url }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Confirm&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;If you did not request this email you can safely ignore it.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those curly brackets are &lt;a href="https://handlebarsjs.com/guide/#what-is-handlebars" rel="noopener noreferrer"&gt;handlebars expressions&lt;/a&gt; and you will provide the &lt;code&gt;context&lt;/code&gt; later while sending an email.&lt;/p&gt;

&lt;p&gt;When you build your Nest application you will notice that the build output is missing your template files (&lt;code&gt;dist/mail/templates&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Fsend-emails-with-nestjs%2Foptimized%2Fhandlebars-templates-missing-in-compilation.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Fsend-emails-with-nestjs%2Foptimized%2Fhandlebars-templates-missing-in-compilation.png" alt="handlebars templates missing in compilation output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, Nest &lt;strong&gt;only&lt;/strong&gt; distributes TypeScript compiled files (&lt;code&gt;.js&lt;/code&gt; and &lt;code&gt;.d.ts&lt;/code&gt;) during the build step. To distribute your &lt;code&gt;.hbs&lt;/code&gt; files, open your &lt;code&gt;nest-cli.json&lt;/code&gt; and add your &lt;code&gt;templates&lt;/code&gt; directory to the &lt;a href="https://docs.nestjs.com/cli/monorepo#assets" rel="noopener noreferrer"&gt;assets&lt;/a&gt; property in the global &lt;code&gt;compilerOptions&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"collection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@nestjs/schematics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sourceRoot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mail/templates/**/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;👈&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/*.hbs"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ending&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.hbs&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"watchAssets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;🤖&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;assets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;watch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;mode&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build your Nest application again and now your template files are included in the build output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Fsend-emails-with-nestjs%2Foptimized%2Fhandlebars-templates-included-in-compilation.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Fsend-emails-with-nestjs%2Foptimized%2Fhandlebars-templates-included-in-compilation.png" alt="handlebars templates included in compilation output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending Mail
&lt;/h2&gt;

&lt;p&gt;Add &lt;code&gt;MailerService&lt;/code&gt; to your own &lt;code&gt;MailService&lt;/code&gt; and implement your mailing logic here. Let's send a user confirmation email using the template &lt;code&gt;confirmation.hbs&lt;/code&gt;. You need to provide &lt;code&gt;{{ name }}&lt;/code&gt; and &lt;code&gt;{{ url }}&lt;/code&gt; under the &lt;code&gt;context&lt;/code&gt; key. Read the &lt;a href="https://handlebarsjs.com/guide" rel="noopener noreferrer"&gt;Handlebars documentation&lt;/a&gt; for more background like &lt;a href="https://handlebarsjs.com/guide/#nested-input-objects" rel="noopener noreferrer"&gt;Nested input objects&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailerService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs-modules/mailer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./../user/user.entity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MailService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;mailerService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MailerService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;sendUserConfirmation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`example.com/auth/confirm?token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mailerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMail&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// from: '"Support Team" &amp;lt;support@example.com&amp;gt;', // override default from&lt;/span&gt;
      &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome to Nice App! Confirm your Email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// `.hbs` extension is appended automatically&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// ✏️ filling curly brackets with content&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ./../user/user.entity&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Mail Service
&lt;/h2&gt;

&lt;p&gt;Add the &lt;code&gt;MailModule&lt;/code&gt; to the &lt;code&gt;imports&lt;/code&gt; list of your modules which need to use the &lt;code&gt;MailService&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthController&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./auth.controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./auth.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./mail/mail.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MailModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// 📧&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AuthController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AuthService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can add &lt;code&gt;MailService&lt;/code&gt; to the constructor of your controllers, resolvers and services&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./../mail/mail.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./../user/user.entity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;mailService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MailService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;signUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// create user in db&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="c1"&gt;// send confirmation mail&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mailService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendUserConfirmation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Move configurations to dotenv file
&lt;/h2&gt;

&lt;p&gt;Currently, the mail server configurations are hardcoded in to the &lt;code&gt;MailModule&lt;/code&gt;. Nest provides a &lt;a href="https://docs.nestjs.com/techniques/configuration" rel="noopener noreferrer"&gt;configuration module&lt;/a&gt; which enables you to load your configurations and credentials from &lt;code&gt;.env&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Install the &lt;code&gt;@nestjs/config&lt;/code&gt; dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# config &lt;/span&gt;
npm i &lt;span class="nt"&gt;--save&lt;/span&gt; @nestjs/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;code&gt;ConfigModule&lt;/code&gt; to the &lt;code&gt;imports&lt;/code&gt; list of your &lt;code&gt;AppModule&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConfigModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppController&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./auth/auth.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;ConfigModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;isGlobal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// no need to import into other modules&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;AuthModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in your root directory and don't forget to add in your &lt;code&gt;.gitingore&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# mail
MAIL_HOST=smtp.example.com
MAIL_USER=user@example.com
MAIL_PASSWORD=topsecret
MAIL_FROM=noreply@example.com

# optional
MAIL_TRANSPORT=smtp://${MAIL_USER}:${MAIL_PASSWORD}@${MAIL_HOST}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reopen &lt;code&gt;MailModule&lt;/code&gt; and change &lt;code&gt;MailerModule.forRoot&lt;/code&gt; to &lt;code&gt;MailerModule.forRootAsync&lt;/code&gt;, this allows you to inject and use the &lt;code&gt;ConfigService&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailerModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs-modules/mailer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HandlebarsAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs-modules/mailer/dist/adapters/handlebars.adapter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./mail.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConfigModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;MailerModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRootAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// imports: [ConfigModule], // import module if not enabled globally&lt;/span&gt;
      &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="c1"&gt;// transport: config.get("MAIL_TRANSPORT"),&lt;/span&gt;
        &lt;span class="c1"&gt;// or&lt;/span&gt;
        &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIL_HOST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIL_USER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIL_PASSWORD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`"No Reply" &amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAIL_USER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;templates&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HandlebarsAdapter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MailService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MailService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MailModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time to add your own mail server configuration, start Nest and send your first mails 📧 to your users&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>email</category>
      <category>handlebars</category>
    </item>
    <item>
      <title>Tailwind CSS Purge: Optimize Angular for Production</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Tue, 15 Dec 2020 09:55:51 +0000</pubDate>
      <link>https://forem.com/notiz_dev/tailwind-css-purge-optimize-angular-for-production-3bc</link>
      <guid>https://forem.com/notiz_dev/tailwind-css-purge-optimize-angular-for-production-3bc</guid>
      <description>&lt;p&gt;By design, &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt; generates a &lt;strong&gt;large amount&lt;/strong&gt; of utility classes for your development build. For your Angular application you want the best performance by &lt;strong&gt;only&lt;/strong&gt; including the classes you are actually using in your production build. Tailwind got you covered! &lt;a href="https://purgecss.com/"&gt;PurgeCSS&lt;/a&gt; is build-in making it easy to tree-shake &lt;strong&gt;unused&lt;/strong&gt; Tailwind styles for your application.&lt;/p&gt;

&lt;p&gt;You are going to learn how to setup Tailwind's &lt;code&gt;purge&lt;/code&gt; option to &lt;a href="https://tailwindcss.com/docs/optimizing-for-production"&gt;optimize&lt;/a&gt; Tailwind CSS in your &lt;a href="https://notiz.dev/blog/angular-10-with-tailwindcss"&gt;Angular&lt;/a&gt; and for your &lt;a href="https://notiz.dev/blog/jamstack-angular-scully-tailwind-css"&gt;Scully&lt;/a&gt; Jamstack application.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;purge&lt;/code&gt; options in this post have been tested with Angular 11 and Tailwind CSS 2.0, it also works with Angular 10 and Tailwind CSS 1.9.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;Get ready with a new or existing Angular + Tailwind CSS application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new app-name

&lt;span class="c"&gt;# add tailwind&lt;/span&gt;
ng add ngx-tailwind

&lt;span class="c"&gt;# optional - add jamstack with Scully&lt;/span&gt;
ng add @scullyio/init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;a href="https://notiz.dev/blog/angular-10-with-tailwindcss#use-tailwind-css-utility-classes"&gt;use Tailwind CSS utility classes&lt;/a&gt; in your Angular application HTML template, using &lt;code&gt;apply&lt;/code&gt; in your stylesheets or even in your TypeScript files via &lt;code&gt;@HostBinding(...)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Purge unused Tailwind CSS utilities in Angular
&lt;/h2&gt;

&lt;p&gt;Tailwind provides a &lt;a href="https://tailwindcss.com/docs/optimizing-for-production#removing-unused-css"&gt;purge&lt;/a&gt; option in the &lt;code&gt;tailwind.config.js&lt;/code&gt; file. Purge removes &lt;strong&gt;only&lt;/strong&gt; classes generated by &lt;a href="https://tailwindcss.com/docs/optimizing-for-production#removing-all-unused-styles"&gt;Tailwind&lt;/a&gt; or styles added to the &lt;code&gt;@layer&lt;/code&gt; &lt;a href="https://tailwindcss.com/docs/functions-and-directives#layer"&gt;directive&lt;/a&gt;. Custom CSS or third-party CSS like Angular Material or Prism.js will not be removed.&lt;/p&gt;

&lt;p&gt;Simply provide all your template paths as an array to the &lt;code&gt;purge&lt;/code&gt; option. For an Angular application this would be all HTML and TS files in your &lt;code&gt;src&lt;/code&gt; directory. TS files should be included as they might reference class names using e.g. &lt;code&gt;@HostBinding(...)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/ **/*.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/** /*.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'media' or 'class'&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;*.{html,ts}&lt;/code&gt; to match multiple file types in the same directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;&lt;span class="gd"&gt;- purge: ["./src/ **/*.html", "./src/** /*.ts"],
&lt;/span&gt;&lt;span class="gi"&gt;+ purge: ["./src/**/*.{html,ts}"],
&lt;/span&gt;  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
&lt;span class="err"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trigger Tailwind to automatically purge your CSS by setting &lt;code&gt;NODE_ENV=production&lt;/code&gt; during your &lt;code&gt;ng build&lt;/code&gt; step. If you used &lt;a href="https://github.com/notiz-dev/ngx-tailwind"&gt;ngx-tailwind&lt;/a&gt; schematics to setup Tailwind it automatically added a production script to your &lt;strong&gt;package.json&lt;/strong&gt;. Additionally, the latest release of &lt;a href="https://github.com/notiz-dev/ngx-tailwind/releases/tag/v1.1.0"&gt;ngx-tailwind@1.1.0&lt;/a&gt; adds the above purge options automatically to your &lt;code&gt;tailwind.config.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build:prod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NODE_ENV=production ng build --prod"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run &lt;code&gt;npm run build:prod&lt;/code&gt; to &lt;strong&gt;only&lt;/strong&gt; include used Tailwind CSS utilities in your Angular production build. This even works great in your Scully application&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Purge options
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;purge&lt;/code&gt; also accepts an options object for further optimizations. Available &lt;code&gt;purge&lt;/code&gt; options are &lt;a href="https://tailwindcss.com/docs/optimizing-for-production#enabling-manually"&gt;enabled&lt;/a&gt;, &lt;code&gt;content&lt;/code&gt; for your template paths, &lt;a href="https://tailwindcss.com/docs/optimizing-for-production#preserving-html-elements"&gt;preserveHtmlElements&lt;/a&gt;, &lt;a href="https://tailwindcss.com/docs/optimizing-for-production#purging-specific-layers"&gt;layers&lt;/a&gt;, &lt;a href="https://tailwindcss.com/docs/optimizing-for-production#purging-specific-layers"&gt;mode&lt;/a&gt; and last &lt;a href="https://tailwindcss.com/docs/optimizing-for-production#purge-css-options"&gt;options&lt;/a&gt; to pass it directly to PurgeCSS. The defaults for these options are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// enabled: true, // enabled by `NODE_ENV=production` or enable manually&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;layers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'all' ☠️ be careful&lt;/span&gt;
    &lt;span class="na"&gt;preserveHtmlElements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or false ⚠️ not generally recommended&lt;/span&gt;
    &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utilities&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// remove layers to ignore from purging&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="c1"&gt;// add your template paths&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* PurgeCSS options */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'media' or 'class'&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Too use the object syntax for the &lt;code&gt;purge&lt;/code&gt; option add the template paths to the &lt;code&gt;content&lt;/code&gt; option&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/**/*.{html,ts}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'media' or 'class'&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and provide additional options to the &lt;code&gt;purge&lt;/code&gt; object to further optimize your production build based on your application. For specific configurations pass it directly to &lt;a href="https://purgecss.com/configuration.html#options"&gt;PurgeCSS&lt;/a&gt; using the &lt;code&gt;options&lt;/code&gt; key. You can provide &lt;code&gt;safelist&lt;/code&gt;, &lt;code&gt;blocklist&lt;/code&gt;, &lt;code&gt;extractors&lt;/code&gt; and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/ **/*.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/** /*.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="c1"&gt;// These options are passed through directly to PurgeCSS&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;safelist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-red-500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/^mat-/&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;blocklist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-orange-500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/^cdk-/&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;extractors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'media' or 'class'&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's improve purging for a Scully application by writing an &lt;code&gt;extractor&lt;/code&gt; for your Markdown content files to detect which HTML tags and CSS classes are &lt;strong&gt;actually&lt;/strong&gt; used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Purge Scully Markdown content
&lt;/h2&gt;

&lt;p&gt;Scully organizes the content of your static-site in Markdown files. Add the path to your Markdown files e.g. &lt;code&gt;'./blog/**/*.md'&lt;/code&gt; to the &lt;code&gt;content&lt;/code&gt; array. Create an &lt;a href="https://purgecss.com/extractors.html#default-extractor"&gt;extractor&lt;/a&gt; targeting only files with the Markdown extension &lt;code&gt;md&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/ **/*.{html,ts}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./blog/** /*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;extractors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;extractor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'media' or 'class'&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before matching HTML elements and CSS classes you need to parse the Markdown content to HTML. Scully uses &lt;a href="https://github.com/markedjs/marked"&gt;marked&lt;/a&gt; to parse your Markdown content files. Let's require it in the &lt;code&gt;tailwind.config.js&lt;/code&gt; and parse the content in the extractor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/ **/*.{html,ts}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./blog/** /*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;extractors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;extractor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'media' or 'class'&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's use the Regex used by &lt;a href="https://github.com/tailwindlabs/blog.tailwindcss.com/blob/364b3713862b6d309aff79505776a9948ad26620/tailwind.config.js#L5-L27"&gt;blog.tailwindcss.com&lt;/a&gt; to find all used HTML elements and classes. Also set the &lt;code&gt;mode: 'all'&lt;/code&gt; ☠️ and &lt;code&gt;preserveHtmlElements: false&lt;/code&gt; ⚠️ to remove unused tags like &lt;code&gt;h4&lt;/code&gt; and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;preserveHtmlElements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/ **/*.{html,ts}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./blog/** /*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;extractors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;extractor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Capture as liberally as possible, including things like `h-(screen-1.5)`&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;broadMatches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;"'`&lt;/span&gt;&lt;span class="se"&gt;\s]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;"'`&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;:&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

            &lt;span class="c1"&gt;// Capture classes within other delimiters like .block(class="w-1/2") in Pug&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;innerMatches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
              &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;"'`&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;.(){}[&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="sr"&gt;#=%&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;"'`&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;.(){}[&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="sr"&gt;#=%:&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;broadMatches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;innerMatches&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'media' or 'class'&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect, now your Angular or Scully applications are &lt;strong&gt;optimized&lt;/strong&gt; for production and you are ready to deploy it to &lt;a href="https://dev.to/blog/firebase-hosting-preview-deploy"&gt;Firebase Hosting&lt;/a&gt; or other services.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>angular</category>
      <category>scully</category>
    </item>
    <item>
      <title>Firebase Hosting: Preview and Deploy via GitHub Actions</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Mon, 09 Nov 2020 13:18:24 +0000</pubDate>
      <link>https://forem.com/notiz_dev/firebase-hosting-preview-and-deploy-via-github-actions-2b5f</link>
      <guid>https://forem.com/notiz_dev/firebase-hosting-preview-and-deploy-via-github-actions-2b5f</guid>
      <description>&lt;p&gt;You start building an &lt;a href="https://notiz.dev/blog/angular-10-with-tailwindcss" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; or a &lt;a href="https://notiz.dev/blog/jamstack-angular-scully-tailwind-css" rel="noopener noreferrer"&gt;Scully&lt;/a&gt; application and at some point you want to invite colleagues, friends, family or customers to check it out. &lt;a href="https://firebase.google.com/docs/hosting/use-cases#what_is_firebase_hosting" rel="noopener noreferrer"&gt;Firebase Hosting&lt;/a&gt; allows to host your static or dynamic web apps for &lt;strong&gt;FREE&lt;/strong&gt; 💸. You are setting up a GitHub workflow deploying your Scully app (works with Angular and any other web framework) to &lt;strong&gt;preview&lt;/strong&gt; and &lt;strong&gt;live&lt;/strong&gt; channel.&lt;/p&gt;

&lt;p&gt;Demo &lt;a href="https://github.com/notiz-dev/angular-scully-tailwindcss" rel="noopener noreferrer"&gt;source code&lt;/a&gt; and &lt;a href="https://angular-scully-tailwindcss.web.app/blog" rel="noopener noreferrer"&gt;hosted&lt;/a&gt; on Firebase 🔥.&lt;/p&gt;

&lt;p&gt;Before you dive straight into this you will need a&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://console.firebase.google.com/" rel="noopener noreferrer"&gt;Firebase Account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/join" rel="noopener noreferrer"&gt;GitHub Account&lt;/a&gt; and a private or public repo for your web app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use your existing web application or create a new one by following &lt;a href="https://notiz.dev/blog/angular-10-with-tailwindcss" rel="noopener noreferrer"&gt;Angular 10 with Tailwind CSS&lt;/a&gt; or &lt;a href="https://notiz.dev/blog/jamstack-angular-scully-tailwind-css" rel="noopener noreferrer"&gt;Jamstack: Angular + Scully + Tailwind CSS&lt;/a&gt; to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Install &lt;a href="https://firebase.google.com/docs/cli#install_the_firebase_cli" rel="noopener noreferrer"&gt;Firebase CLI&lt;/a&gt; minimum in &lt;code&gt;v8.12.0&lt;/code&gt; for &lt;a href="https://firebase.googleblog.com/2020/10/preview-channels-firebase-hosting.html" rel="noopener noreferrer"&gt;preview channels&lt;/a&gt; support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install firebase cli&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; firebase-tools

&lt;span class="c"&gt;# init firebase hosting&lt;/span&gt;
firebase init hosting
&lt;span class="c"&gt;# hosting already setup, prepare GitHub workflow&lt;/span&gt;
firebase init hosting:github
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the CLI prompts to setup Firebase hosting and GitHub workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firebase Hosting Setup
&lt;/h2&gt;

&lt;p&gt;Start by selecting an &lt;strong&gt;existing&lt;/strong&gt; Firebase project, create one in &lt;a href="https://console.firebase.google.com/" rel="noopener noreferrer"&gt;Firebase console&lt;/a&gt;, or create a &lt;strong&gt;new&lt;/strong&gt; project through the CLI.&lt;/p&gt;

&lt;p&gt;Next enter the public directory containing all files of your web app including &lt;code&gt;index.html&lt;/code&gt; which is uploaded to Firebase hosting&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Angular&lt;/span&gt;
dist/&amp;lt;project-name&amp;gt;

&lt;span class="c"&gt;# Scully `outDir` specified in your scully.&amp;lt;project-name&amp;gt;.ts defaults to&lt;/span&gt;
dist/static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can change the &lt;code&gt;public&lt;/code&gt; directory anytime in &lt;code&gt;firebase.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Answer the next question "Configure as a single-page app (rewrite all urls to /index.html)?" with &lt;strong&gt;yes&lt;/strong&gt; for Angular apps (and other single-page apps) and &lt;strong&gt;no&lt;/strong&gt; for Scully apps (and other static-site apps).&lt;/p&gt;

&lt;p&gt;Let Firebase CLI initialize your GitHub repository for &lt;a href="https://firebase.google.com/docs/hosting/github-integration#set-up" rel="noopener noreferrer"&gt;automatic deploys&lt;/a&gt;. Several steps are taken care by the Firebase CLI for you&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a Firebase service account with deployment permissions to Firebase Hosting&lt;/li&gt;
&lt;li&gt;Encrypt and add secret to GitHub repository&lt;/li&gt;
&lt;li&gt;Creating GitHub workflow &lt;code&gt;yaml&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enter &lt;strong&gt;no&lt;/strong&gt; for the next two questions to overwrite &lt;code&gt;dist/static/404.html&lt;/code&gt; and &lt;code&gt;dist/static/index.html&lt;/code&gt;, let those be generated by Scully.&lt;/p&gt;

&lt;p&gt;Select a GitHub repository to setup your secret token for your workflow and enter a build script to build Angular and Scully like &lt;code&gt;npm ci &amp;amp;&amp;amp; npm run build:ci&lt;/code&gt;. For a Scully build add the following two scripts to your &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"build:ci"&lt;/span&gt;: &lt;span class="s2"&gt;"npm run build:prod &amp;amp;&amp;amp; npm run scully:ci"&lt;/span&gt;
&lt;span class="s2"&gt;"scully:ci"&lt;/span&gt;: &lt;span class="s2"&gt;"scully -- --host='0.0.0.0' --scanRoutes --serverTimeout=60000"&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you like to deploy to live channel on merged Pull Request answer with &lt;strong&gt;yes&lt;/strong&gt; and enter your branch name for the live channel for example &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Workflow
&lt;/h2&gt;

&lt;p&gt;You should have now two workflows if you used the Firebase CLI. The workflows use the GitHub Action &lt;a href="https://github.com/marketplace/actions/deploy-to-firebase-hosting" rel="noopener noreferrer"&gt;Deploy to Firebase Hosting&lt;/a&gt;, currently in &lt;strong&gt;alpha&lt;/strong&gt; release.&lt;/p&gt;

&lt;p&gt;Workflow to deploy to a preview channel on Pull Request &lt;code&gt;.github/workflows/firebase-hosting-pull-request.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This file was auto-generated by the Firebase CLI&lt;/span&gt;
&lt;span class="c1"&gt;# https://github.com/firebase/firebase-tools&lt;/span&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Firebase Hosting on PR&lt;/span&gt;
&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;on'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull_request&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build_and_preview&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ci&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build:ci'&lt;/span&gt;
      &lt;span class="c1"&gt;# Add additional build steps here&lt;/span&gt;
      &lt;span class="c1"&gt;# - run: ...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FirebaseExtended/action-hosting-deploy@v0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;repoToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.GITHUB_TOKEN&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
          &lt;span class="na"&gt;firebaseServiceAccount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.FIREBASE_SERVICE_ACCOUNT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
          &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-firebase-project-id&lt;/span&gt;
          &lt;span class="c1"&gt;# default expire value 7 days&lt;/span&gt;
          &lt;span class="c1"&gt;# expires: 7d&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;FIREBASE_CLI_PREVIEWS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hostingchannels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Workflow to deploy to your live channel on push to &lt;code&gt;main&lt;/code&gt; branch &lt;code&gt;.github/workflows/firebase-hosting-merge.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This file was auto-generated by the Firebase CLI&lt;/span&gt;
&lt;span class="c1"&gt;# https://github.com/firebase/firebase-tools&lt;/span&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Firebase Hosting on merge&lt;/span&gt;
&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;on'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build_and_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ci&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build:ci'&lt;/span&gt;
      &lt;span class="c1"&gt;# Add additional build steps here&lt;/span&gt;
      &lt;span class="c1"&gt;# - run: ...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FirebaseExtended/action-hosting-deploy@v0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;repoToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.GITHUB_TOKEN&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
          &lt;span class="na"&gt;firebaseServiceAccount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.FIREBASE_SERVICE_ACCOUNT&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
          &lt;span class="na"&gt;channelId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;live&lt;/span&gt;
          &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-firebase-project-id&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;FIREBASE_CLI_PREVIEWS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hostingchannels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploying to the live channel requires &lt;code&gt;channelId&lt;/code&gt; set to &lt;code&gt;live&lt;/code&gt;. If left blank the action creates a new preview channel using the PR-branch name.&lt;/p&gt;

&lt;p&gt;Additional option for the preview channel is &lt;code&gt;expires&lt;/code&gt; which defaults to 7 days. Change the &lt;a href="https://firebase.google.com/docs/hosting/manage-hosting-resources#preview-channel-expiration" rel="noopener noreferrer"&gt;expiration&lt;/a&gt; of your preview channel to maximum 30 days. It supports the syntax &lt;code&gt;h&lt;/code&gt; for hours, &lt;code&gt;d&lt;/code&gt; for days and &lt;code&gt;w&lt;/code&gt; for weeks, for example &lt;code&gt;19h&lt;/code&gt;, &lt;code&gt;30d&lt;/code&gt;, &lt;code&gt;3w&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview and Live Channel
&lt;/h2&gt;

&lt;p&gt;Create a Pull Request with the above GitHub workflows and you should see the GitHub Action start building&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffirebase-hosting-preview-deploy%2Foptimized%2Fgithub-action-pull-request-building.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffirebase-hosting-preview-deploy%2Foptimized%2Fgithub-action-pull-request-building.png" alt="GitHub Action run on Pull Request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the workflow finished successfully, the Firebase action creates a comment with the preview URL for this PR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffirebase-hosting-preview-deploy%2Foptimized%2Fpreview-url-created-by-firebase-action.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffirebase-hosting-preview-deploy%2Foptimized%2Fpreview-url-created-by-firebase-action.png" alt="Preview URL created by Firebase Action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;View the preview of your web app, if you are not happy with your changes repeat it. Here is the Scully demo blog in the preview channel on Firebase Hosting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffirebase-hosting-preview-deploy%2Foptimized%2Fscully-preview-on-firebase-hosting.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffirebase-hosting-preview-deploy%2Foptimized%2Fscully-preview-on-firebase-hosting.png" alt="Preview URL created by Firebase Action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, merge your Pull Request to trigger the deployment to the live channel. Find the &lt;a href="https://angular-scully-tailwindcss.web.app/blog" rel="noopener noreferrer"&gt;Scully demo blog&lt;/a&gt; on the live channel.&lt;/p&gt;

&lt;p&gt;It was never easier to ship improvements to your web application to preview, ask colleagues or customers for a review 👌❓ and simply deploy your changes to the live channel 🚀 by merging your PR.&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>github</category>
      <category>ci</category>
      <category>angular</category>
    </item>
    <item>
      <title>Jamstack: Angular + Scully + Tailwind CSS</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Tue, 27 Oct 2020 11:46:18 +0000</pubDate>
      <link>https://forem.com/notiz_dev/jamstack-angular-scully-tailwind-css-172a</link>
      <guid>https://forem.com/notiz_dev/jamstack-angular-scully-tailwind-css-172a</guid>
      <description>&lt;p&gt;&lt;a href="https://scully.io/"&gt;Scully&lt;/a&gt; is a static site generator build for the web framework &lt;a href="https://angular.io/"&gt;Angular&lt;/a&gt;. You learn how to build a &lt;a href="https://jamstack.org/what-is-jamstack/"&gt;Jamstack&lt;/a&gt; project with Angular and Scully. Let's add &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt; for easy styling, the cherry 🍒 on the cake 🍰.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Create a new Angular project, add Scully and Tailwind CSS using schematics. Angular provides &lt;a href="https://angular.io/guide/schematics"&gt;schematics&lt;/a&gt; for generating and performing installation steps automatically for you - used by Scully and &lt;a href="https://github.com/notiz-dev/ngx-tailwind"&gt;ngx-tailwind&lt;/a&gt;. 💯&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new app-name &lt;span class="nt"&gt;--style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;scss &lt;span class="nt"&gt;--routing&lt;/span&gt; &lt;span class="nb"&gt;true
cd &lt;/span&gt;app-name

&lt;span class="c"&gt;# add scully&lt;/span&gt;
ng add @scullyio/init

&lt;span class="c"&gt;# add scully blog&lt;/span&gt;
ng generate @scullyio/init:blog

&lt;span class="c"&gt;# add tailwind&lt;/span&gt;
ng add ngx-tailwind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or follow along with the &lt;a href="https://github.com/notiz-dev/angular-scully-tailwindcss"&gt;source code&lt;/a&gt;. For more information about Angular and Tailwind read the in-depth guide &lt;a href="https://notiz.dev/blog/angular-10-with-tailwindcss"&gt;Angular 10 with Tailwind CSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove unused CSS for production build 🧹
&lt;/h2&gt;

&lt;p&gt;Tailwind generates a lot of CSS styles which are very helpful during development. Before deploying the Scully app &lt;a href="https://tailwindcss.com/docs/controlling-file-size#removing-unused-css"&gt;remove all unused CSS&lt;/a&gt; using the &lt;code&gt;purge&lt;/code&gt; option in &lt;code&gt;tailwind.config.js&lt;/code&gt;. Provide paths of your template and TypeScript files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;  future: {
    // removeDeprecatedGapUtilities: true,
    // purgeLayersByDefault: true,
  },
&lt;span class="gi"&gt;+ purge: ["./src/ **/*.html", "./src/** /*.ts"],
&lt;/span&gt;  theme: {
    extend: {},
  },
  variants: {},
  plugins: [],
&lt;span class="err"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tailwind automatically purges unused styles when &lt;code&gt;NODE_ENV&lt;/code&gt; is set to &lt;code&gt;production&lt;/code&gt;. Use the script added by &lt;code&gt;ngx-tailwind&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build:prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;app.component.html&lt;/code&gt; and add a &lt;code&gt;header&lt;/code&gt; template using Tailwind utility styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-3xl mx-auto px-4 sm:px-6 xl:max-w-5xl xl:px-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-between items-center py-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-xl font-semibold"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Angular + Scully + Tailwind = 🚀 &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/notiz-dev/angular-scully-tailwindcss"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-medium text-gray-500 hover:text-gray-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Source Code &lt;span class="ni"&gt;&amp;amp;rarr;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Displaying Angular routes --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Customize your own header on &lt;a href="https://play.tailwindcss.com/CyNMJvURiy"&gt;play.tailwindcss.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Scully
&lt;/h2&gt;

&lt;p&gt;Getting started to serve Angular as a Jamstack app for the first time. Follow the steps in this order.&lt;/p&gt;

&lt;p&gt;First build Angular app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build:prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build Scully app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run scully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All pre-rendered static site files are generated in &lt;code&gt;./dist/static&lt;/code&gt;. For each page you will see an &lt;code&gt;index.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Scully app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run scully:serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your favorite browser and go to &lt;a href="http://localhost:1668/"&gt;http://localhost:1668/&lt;/a&gt; for the Scully static server and &lt;a href="http://localhost:1864/"&gt;http://localhost:1864/&lt;/a&gt; for the Angular server.&lt;/p&gt;

&lt;p&gt;You should see on both links - Angular top 👆, Scully bottom 👇.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xQmfgJLS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/first-scully-serve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xQmfgJLS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/first-scully-serve.png" alt="First Scully Serve"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's added by Scully? 🔍
&lt;/h2&gt;

&lt;p&gt;Scully creates a &lt;a href="https://scully.io/docs/reference/config/"&gt;config&lt;/a&gt; file &lt;code&gt;scully.&amp;lt;projectname&amp;gt;.config.ts&lt;/code&gt; at the root folder. You will look at this in a moment.&lt;/p&gt;

&lt;p&gt;Additionally, after your first Scully build new few new files are generated by Scully. Let's add those to the &lt;code&gt;.gitignore&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scully&lt;/span&gt;
.scully
scully.log
src/assets/scully-routes.json
scully/plugins/&lt;span class="k"&gt;*&lt;/span&gt;.js
scully/plugins/&lt;span class="k"&gt;*&lt;/span&gt;.js.map
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;scully-routes.json&lt;/code&gt; contains all information about your available routes including the front matter data which you will access later in your blog post page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown blog
&lt;/h2&gt;

&lt;p&gt;One 💍 command to rule your blog&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate @scullyio/init:blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do you need more flexibility? Try running the following and answer a few &lt;a href="https://scully.io/docs/learn/create-a-blog/add-blog-support/#available-options"&gt;questions&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate @scullyio/init:markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your blog is setup. A new route like &lt;code&gt;/blog/&amp;lt;slug&amp;gt;&lt;/code&gt; has been added, re-build your Angular app and discover the new route with Scully&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run scully &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--scanRoutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Serve your Scully app and navigate to the first generated post in your browser &lt;code&gt;localhost:1668/blog/&amp;lt;slug&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GEWOnF1a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/first-scully-blog-post.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GEWOnF1a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/first-scully-blog-post.png" alt="First Scully Blog Post"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scully creates for each blog post an &lt;code&gt;index.html&lt;/code&gt; inside your &lt;code&gt;dist/static/blog&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IPngvczv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/scully-build-with-unpublished-post.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IPngvczv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/scully-build-with-unpublished-post.png" alt="Scully Static Directory with unpublished post"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Style blog post page
&lt;/h2&gt;

&lt;p&gt;Alright, you got your first blog post page rendered, but let's be honest 🧐 it needs a few adjustment. The current blog post page is missing the &lt;strong&gt;blog post title&lt;/strong&gt; , &lt;strong&gt;publishing date&lt;/strong&gt; and has &lt;strong&gt;unstyled&lt;/strong&gt; content.&lt;/p&gt;

&lt;p&gt;Scully has a build in service &lt;code&gt;ScullyRoutesService&lt;/code&gt; to access the front matter of your blog posts. It exposes all published posts under &lt;code&gt;ScullyRoutesService.available$&lt;/code&gt; and the current page with &lt;code&gt;ScullyRoutesService.getCurrent()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open your generated &lt;code&gt;blog.component.ts&lt;/code&gt; and use &lt;code&gt;ScullyRoutesService.getCurrent()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ViewEncapsulation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ActivatedRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ScullyRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ScullyRoutesService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@scullyio/ng-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./blog.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./blog.component.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;preserveWhitespaces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;encapsulation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ViewEncapsulation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Emulated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;BlogComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;currentPost$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ScullyRoute&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scully&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCurrent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActivatedRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;scully&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ScullyRoutesService&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your template use &lt;code&gt;async&lt;/code&gt; pipe to unwrap the &lt;code&gt;Observable&lt;/code&gt; to access the &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;publishedAt&lt;/code&gt; (custom property).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"currentPost$ | async as currentPost"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pt-6 xl:pb-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"space-y-1 text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;dl&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"space-y-10"&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"currentPost.publishedAt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;dt&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sr-only"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Published on&lt;span class="nt"&gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;dd&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-base leading-6 font-medium text-gray-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;time&lt;/span&gt; &lt;span class="na"&gt;[dateTime]=&lt;/span&gt;&lt;span class="s"&gt;"currentPost.publishedAt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; {{ currentPost.publishedAt | date: "dd MMMM yyyy" }} &lt;span class="nt"&gt;&amp;lt;/time&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/dd&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl leading-9 font-extrabold text-gray-900 tracking-tight sm:text-4xl sm:leading-10 md:text-5xl md:leading-14"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ currentPost.title }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- This is where Scully will inject the static HTML --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;scully-content&amp;gt;&amp;lt;/scully-content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out &lt;a href="https://blog.tailwindcss.com/"&gt;Tailwind Blog&lt;/a&gt; which is used here as reference for styling a simple and clean blog.&lt;/p&gt;

&lt;p&gt;Update your blog front matter to set &lt;code&gt;published&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;, add &lt;code&gt;publishedAt&lt;/code&gt; with a datetime string and remove any unpublished &lt;code&gt;slug&lt;/code&gt;, also add some placeholder content:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
--------
title: 2020-10-23-blog
description: 10 Top tips about your next Pizza Hawaii
published: true
publishedAt: 2020-10-31T13:37:00.000Z
--------

# Pizza 🍕

Eat **pizza** hawaii *everyday*. ~~Don't forget~~ the 🧀 on your `pizza`.



```
var pizza = "Eat 🍕";
alert(pizza);
```



## Hawaii

Ordered List

1. 🍍
2. 🥓
3. 🧀

Unordered List

* 🧀
* 🍍
* 🥓

### Diavolo

&amp;gt; Pizza might be very 🌶️
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Last but not least, remove all styles from &lt;code&gt;blog.component.scss&lt;/code&gt;. Now re-build Angular, Scully and serve Scully to see the new blog post header&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1NiV8rCe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/scully-post-with-header.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1NiV8rCe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/scully-post-with-header.png" alt="Scully blog post header with title and published date"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome 🤩 looks much better. What about the unstyled content? 🤨 Go and install &lt;a href="https://github.com/tailwindlabs/tailwindcss-typography#installation"&gt;Tailwind Typography&lt;/a&gt; plugin applying styles to your markdown content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @tailwindcss/typography
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the plugin to your &lt;code&gt;tailwind.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// removeDeprecatedGapUtilities: true,&lt;/span&gt;
    &lt;span class="c1"&gt;// purgeLayersByDefault: true,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/ **/*.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/** /*.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tailwindcss/typography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now wrap &lt;code&gt;&amp;lt;scully-content&amp;gt;&amp;lt;/scully-content&amp;gt;&lt;/code&gt; with the &lt;code&gt;prose&lt;/code&gt; class provided by the Tailwind Typography plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"prose max-w-none pt-10 pb-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- This is where Scully will inject the static HTML --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scully-content&amp;gt;&amp;lt;/scully-content&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blog post content is now styled 😎. How easy was that? Feel free to further &lt;a href="https://github.com/tailwindlabs/tailwindcss-typography#customization"&gt;customize&lt;/a&gt; the styles to your brand or personal style.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EP6w8AVJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/scully-typography-prose-content.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EP6w8AVJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/scully-typography-prose-content.png" alt="Prose markdown content using Tailwind Typography plugin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One more thing is left, listing all available posts and navigating to the post slug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blog Overview Page
&lt;/h2&gt;

&lt;p&gt;Generate a new component for your route &lt;code&gt;/blog&lt;/code&gt; displaying all available posts using &lt;code&gt;ScullyRoutesService.available$&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng g module blogs &lt;span class="nt"&gt;--route&lt;/span&gt; blogs &lt;span class="nt"&gt;--module&lt;/span&gt; blog/blog.module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the path of your new route in &lt;code&gt;blog-routing.module.ts&lt;/code&gt; from &lt;code&gt;blogs&lt;/code&gt; to empty to match the &lt;code&gt;/blog&lt;/code&gt; route.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const routes: Routes = [
&lt;/span&gt;  {
&lt;span class="gi"&gt;+ path: '',
&lt;/span&gt;&lt;span class="gd"&gt;- path: 'blogs',
&lt;/span&gt;    loadChildren: () =&amp;gt;
      import('../blogs/blogs.module').then((m) =&amp;gt; m.BlogsModule),
  },
  {
    path: ':slug',
    component: BlogComponent,
  },
  {
    path: '**',
    component: BlogComponent,
  },
&lt;span class="err"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you like to automatically redirect to &lt;code&gt;/blog&lt;/code&gt; open &lt;code&gt;app-routing.module.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const routes: Routes = [
&lt;/span&gt;&lt;span class="gi"&gt;+ { path: '', redirectTo: 'blog', pathMatch: 'full' },
&lt;/span&gt;  {
    path: 'blog',
    loadChildren: () =&amp;gt; import('./blog/blog.module').then((m) =&amp;gt; m.BlogModule),
  },
&lt;span class="err"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create a reference for all available blog posts in &lt;code&gt;blogs.component.ts&lt;/code&gt; filtering out pages with routes starting only with &lt;code&gt;/blog/&lt;/code&gt;. Additionally, sort your posts in descending order by the &lt;code&gt;publishedAt&lt;/code&gt; date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ScullyRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ScullyRoutesService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@scullyio/ng-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-blogs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./blogs.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./blogs.component.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;BlogsComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;available$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ScullyRoute&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;scully&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ScullyRoutesService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scully&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blog/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
      &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;page1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publishedAt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publishedAt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important to note you have to import &lt;code&gt;ScullyLibModule&lt;/code&gt; in your &lt;code&gt;blogs.module.ts&lt;/code&gt; to access &lt;code&gt;ScullyRoutesService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add your blog name and loop over all posts in your template&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pt-6 pb-8 space-y-2 md:space-y-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl font-extrabold bg-clip-text text-transparent bg-gradient-to-r from-orange-500 via-yellow-400 to-yellow-200 tracking-tight sm:text-4xl md:text-6xl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Company Blog&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-lg leading-7 text-gray-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;All the latest Company news.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"divide-y divide-gray-200"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let post of available$ | async"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"py-12"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"space-y-2 xl:grid xl:grid-cols-4 xl:space-y-0 xl:items-baseline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dt&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sr-only"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Published on&lt;span class="nt"&gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dd&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-base leading-6 font-medium text-gray-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;time&lt;/span&gt; &lt;span class="na"&gt;[dateTime]=&lt;/span&gt;&lt;span class="s"&gt;"post.publishedAt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; {{ post.publishedAt | date: "dd MMMM yyyy" }} &lt;span class="nt"&gt;&amp;lt;/time&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dd&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"space-y-5 xl:col-span-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"space-y-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl leading-8 font-bold tracking-tight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"post.route"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-900 hover:text-gray-700"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; {{ post.title }} &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"prose max-w-none text-gray-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ post.description }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-base leading-6 font-medium"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"post.route"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-orange-500 hover:text-orange-600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Read more &lt;span class="ni"&gt;&amp;amp;rarr;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0ux1q9Zw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/blog-post-overview-page.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0ux1q9Zw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/jamstack-angular-scully-tailwind-css/optimized/blog-post-overview-page.png" alt="Blog overview page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now all you need is an idea and time to write it down. &lt;a href="https://scully.io/docs/learn/create-a-blog/generate-new-blog-posts/"&gt;Creating&lt;/a&gt; your next blog post is as easy as 💆&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate @scullyio/init:post &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Cool post"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Made possible by Scully schematics.&lt;/p&gt;

&lt;p&gt;What are you wait for? 😄 Now it's time for you to create your own blog 🚀. Need more inspiration? &lt;a href="https://notiz.dev"&gt;notiz.dev&lt;/a&gt; is build using Scully and Tailwind CSS. 😉&lt;/p&gt;

</description>
      <category>angular</category>
      <category>scully</category>
      <category>tailwindcss</category>
      <category>css</category>
    </item>
    <item>
      <title>DBML generator for Prisma</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Wed, 16 Sep 2020 08:29:27 +0000</pubDate>
      <link>https://forem.com/notiz_dev/dbml-generator-for-prisma-4kea</link>
      <guid>https://forem.com/notiz_dev/dbml-generator-for-prisma-4kea</guid>
      <description>&lt;p&gt;Introducing 🥳 &lt;a href="https://github.com/notiz-dev/prisma-dbml-generator"&gt;Prisma DBML Generator&lt;/a&gt; &lt;strong&gt;automatically&lt;/strong&gt; generating a &lt;a href="https://www.dbml.org/home"&gt;DBML&lt;/a&gt; schema based on your Prisma Schema.&lt;/p&gt;

&lt;p&gt;Simply install the DBML generator&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; prisma-dbml-generator
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add the generator to your &lt;code&gt;schema.prisma&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generator dbml {
  provider = "prisma-dbml-generator"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;npx prisma generate&lt;/code&gt; for the following Prisma schema&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model User {
  id Int @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  email String @unique
  name String?
  posts Post[]
  profile Profile?
  /// user role
  role Role @default(USER)
}

/// User profile
model Profile {
  id Int @default(autoincrement()) @id
  bio String?
  user User @relation(fields: [userId], references: [id])
  userId Int @unique
}

model Post {
  id Int @id @default(autoincrement())
  title String
  content String?
  published Boolean @default(false)
  author User? @relation(fields: [authorId], references: [id])
  authorId Int?
}

/// user role
enum Role {
  ADMIN /// allowed to do everything
  USER
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;generates the following &lt;code&gt;schema.dbml&lt;/code&gt; to &lt;code&gt;prisma/dbml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table User {
  id Int [pk, increment]
  createdAt DateTime [default: `now()`, not null]
  updatedAt DateTime [not null]
  email String [unique, not null]
  name String
  posts Post
  profile Profile
  role Role [not null, default: 'USER', note: 'user role']
}

Table Profile {
  id Int [pk, increment]
  bio String
  user User [not null]
  userId Int [unique, not null]

  Note: 'User profile'
}

Table Post {
  id Int [pk, increment]
  title String [not null]
  content String
  published Boolean [not null, default: false]
  author User
  authorId Int
}

Enum Role {
  ADMIN
  USER
}

Ref: Profile.userId - User.id

Ref: Post.authorId &amp;gt; User.id
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Copy the &lt;code&gt;schema.dbml&lt;/code&gt; content and &lt;a href="https://dbdiagram.io/d"&gt;visualize&lt;/a&gt; it as an Entity-Relationship Diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Nvt6vpT9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/prisma-dbml-generator/optimized/db-diagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Nvt6vpT9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/prisma-dbml-generator/optimized/db-diagram.png" alt="Entity-Relationship Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see this output each time you run &lt;code&gt;npx prisma generate&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx prisma generate
Environment variables loaded from prisma/.env

✔ Generated Prisma Client to ./node_modules/@prisma/client &lt;span class="k"&gt;in &lt;/span&gt;281ms

✔ Generated DBML Schema to ./prisma/dbml &lt;span class="k"&gt;in &lt;/span&gt;5ms

You can now start using Prisma Client &lt;span class="k"&gt;in &lt;/span&gt;your code:

&lt;span class="sb"&gt;``&lt;/span&gt;
import &lt;span class="o"&gt;{&lt;/span&gt; PrismaClient &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'@prisma/client'&lt;/span&gt;
// or const &lt;span class="o"&gt;{&lt;/span&gt; PrismaClient &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@prisma/client'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

const prisma &lt;span class="o"&gt;=&lt;/span&gt; new PrismaClient&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="sb"&gt;``&lt;/span&gt;

Explore the full API: http://pris.ly/d/client
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/notiz-dev/prisma-dbml-generator#readme"&gt;Check out the readme&lt;/a&gt; and give it a try with your own Prisma Schema 😎.&lt;/p&gt;

</description>
      <category>prisma</category>
      <category>dbml</category>
    </item>
    <item>
      <title>Introducing NestJS Prisma Schematics</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Fri, 07 Aug 2020 10:59:37 +0000</pubDate>
      <link>https://forem.com/notiz_dev/introducing-nestjs-prisma-schematics-3he</link>
      <guid>https://forem.com/notiz_dev/introducing-nestjs-prisma-schematics-3he</guid>
      <description>&lt;p&gt;Until now, adding &lt;a href="https://notiz.dev/blog/how-to-connect-nestjs-with-prisma"&gt;Prisma to a NestJS application&lt;/a&gt; requires a few manual steps - installing &lt;a href="https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-cli/command-reference"&gt;@prisma/cli&lt;/a&gt; and &lt;a href="https://github.com/prisma/prisma-client-js"&gt;@prisma/client&lt;/a&gt;, creating a &lt;code&gt;PrismaService&lt;/code&gt; and (eventually) adding a &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I am excited to release &lt;a href="https://github.com/marcjulian/nestjs-prisma"&gt;&lt;code&gt;nestjs-prisma&lt;/code&gt;&lt;/a&gt; - a set of schematics to perform all steps necessary to add Prisma to your NestJS application &lt;strong&gt;automatically&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;All you need to do is run the following command in your Nest app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;nest add nestjs-prisma
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wbN58wi_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://notiz.dev/assets/img/blog/nestjs-prisma-schematics/schematics-in-action.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wbN58wi_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://notiz.dev/assets/img/blog/nestjs-prisma-schematics/schematics-in-action.gif" alt="Schematics in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you need more options? I got you covered, you can go a step further and specify a Prisma version if you like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;nest add nestjs-prisma &lt;span class="nt"&gt;--prismaVersion&lt;/span&gt; 2.4.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or go crazy by adding a &lt;code&gt;Dockerfile&lt;/code&gt; for your Nest app and a &lt;code&gt;docker-compose.yaml&lt;/code&gt; with a &lt;strong&gt;PostgreSQL&lt;/strong&gt; database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;nest add nestjs-prisma &lt;span class="nt"&gt;--addDocker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/marcjulian/nestjs-prisma#additional-options"&gt;Check out all options&lt;/a&gt; and give it a try with your Nest app.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>prisma</category>
      <category>schematics</category>
    </item>
    <item>
      <title>Dockerizing a NestJS app with Prisma and PostgreSQL</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Fri, 31 Jul 2020 11:43:39 +0000</pubDate>
      <link>https://forem.com/notiz_dev/dockerizing-a-nestjs-app-with-prisma-and-postgresql-3809</link>
      <guid>https://forem.com/notiz_dev/dockerizing-a-nestjs-app-with-prisma-and-postgresql-3809</guid>
      <description>&lt;p&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; 🐳 enables you to build &lt;strong&gt;consistent&lt;/strong&gt; containers of your applications for your development, testing and production environments. In this post you will dockerize a NestJS 😸 application with Prisma connecting to a PostgreSQL 🐘 database.&lt;/p&gt;

&lt;p&gt;Requirements for this post are&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Docker &lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;installed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://notiz.dev/blog/how-to-connect-nestjs-with-prisma" rel="noopener noreferrer"&gt;NestJS application with Prisma&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can find the &lt;a href="https://github.com/notiz-dev/nestjs-prisma-docker" rel="noopener noreferrer"&gt;full source code&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;Use this prisma schema to follow along:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datasource db {
  provider = "postgresql"
  url = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Food {
  id Int @id @default(autoincrement())
  name String
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a &lt;code&gt;.env&lt;/code&gt; file in your &lt;code&gt;prisma&lt;/code&gt; directory for a dummy PostgreSQL connection url:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  TL;DR Multi-stage Dockerfile
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; in the root of your Nest application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;Dockerfile&lt;/code&gt; and use the multi-stage build steps 🤙&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="c"&gt;# Create app directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# A wildcard is used to ensure both package.json AND package-lock.json are copied&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; prisma ./prisma/&lt;/span&gt;

&lt;span class="c"&gt;# Install app dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# Generate prisma client, leave out if generating in `postinstall` script&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npx prisma generate

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:12&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/node_modules ./node_modules&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist ./dist&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "start:prod"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait… what is going in the &lt;code&gt;Dockerfile&lt;/code&gt; 🤔❓ See the breakdown for each step below.&lt;/p&gt;

&lt;p&gt;Don't forget to create a &lt;code&gt;.dockerignore&lt;/code&gt; file next to your &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;node_modules
npm-debug.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;COPY&lt;/code&gt; command ignores those local files and folder and won't copy them into your Docker image to prevent &lt;strong&gt;overwriting&lt;/strong&gt; your installed modules in your image.&lt;/p&gt;

&lt;p&gt;Your application structure should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Fdockerizing-nestjs-with-prisma-and-postgresql%2Foptimized%2Fproject-structure.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Fdockerizing-nestjs-with-prisma-and-postgresql%2Foptimized%2Fproject-structure.png" alt="Project structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Breakdown of the multi-stage Dockerfile
&lt;/h2&gt;

&lt;p&gt;Let's breakdown the &lt;code&gt;Dockerfile&lt;/code&gt; step-by-step&lt;/p&gt;

&lt;p&gt;🏗 Builder Image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line tells Docker to use the latest &lt;a href="https://nodejs.org/en/about/releases/" rel="noopener noreferrer"&gt;LTS&lt;/a&gt; version &lt;code&gt;12&lt;/code&gt; for &lt;code&gt;node&lt;/code&gt; as the base image to build the container from. To optimize the container image size you are using the &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="noopener noreferrer"&gt;multistage-build&lt;/a&gt; and assign a name to your base image &lt;code&gt;AS builder&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : Before updating to a newer version of &lt;code&gt;node&lt;/code&gt; check the support of Nest, Prisma and other dependencies&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🧰 Working directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create app directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the working directory for your application which stores your code. All commands (&lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt;) are executed inside this directory.&lt;/p&gt;

&lt;p&gt;📦 Installation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# A wildcard is used to ensure both package.json AND package-lock.json are copied&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; prisma ./prisma/&lt;/span&gt;

&lt;span class="c"&gt;# Install app dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# Generate prisma client, leave out if generating in `postinstall` script&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npx prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next you need to install your app dependencies inside the Docker image. &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; are copied over. Generating the Prisma Client requires the &lt;code&gt;schema.prisma&lt;/code&gt; file. &lt;code&gt;COPY prisma ./prisma/&lt;/code&gt; copies the whole &lt;code&gt;prisma&lt;/code&gt; directory in case you also need the migrations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : Only &lt;code&gt;package*.json&lt;/code&gt; and &lt;code&gt;prisma&lt;/code&gt; directory is copied in this step to take advantage of the cached Docker layers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Install all dependencies &lt;code&gt;RUN npm install&lt;/code&gt; (dev too). This allows you to build the Nest application inside the Docker image. Now its also the time to generate the Prisma Client. &lt;strong&gt;Leave&lt;/strong&gt; this step out if you are generating the client in the a &lt;code&gt;postinstall&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;⚙️ Build app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To build your Nest application copy all of your source files (exceptions in &lt;code&gt;.dockerignore&lt;/code&gt;) into the Docker image. Now it's time to build your app &lt;code&gt;RUN npm run build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;👟 Run your app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second &lt;code&gt;FROM&lt;/code&gt; is the second stage in the multi-stage build and is used to &lt;strong&gt;run&lt;/strong&gt; your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/node_modules ./node_modules&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist ./dist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy &lt;strong&gt;from&lt;/strong&gt; your &lt;code&gt;builder&lt;/code&gt; image only files and folders required to run the Nest app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "start:prod"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nest apps usually bind to port &lt;code&gt;3000&lt;/code&gt;, &lt;code&gt;EXPOSE&lt;/code&gt; the same port for your Docker image. Last step is the command to run the Nest application using &lt;code&gt;CMD&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build and run your image
&lt;/h2&gt;

&lt;p&gt;Enter the following command in the directory of your &lt;code&gt;Dockerfile&lt;/code&gt;. Give your build image a name using the &lt;code&gt;-t&lt;/code&gt; flag to easily start, stop and remove it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# give your docker image a name&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;your username&amp;gt;/nest-api &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# for example&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; nest-api &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After your Docker image is successfully build start it with this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;--env-file&lt;/span&gt; prisma/.env &lt;span class="nt"&gt;-d&lt;/span&gt; &amp;lt;your username&amp;gt;/nest-api 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prisma Client requires the &lt;code&gt;DATABASE_URL&lt;/code&gt; environment variable which you pass using the &lt;code&gt;--env-file prisma/.env&lt;/code&gt; flag. Use this &lt;code&gt;.env&lt;/code&gt; file for additional environment variables (Port, JWT Secret etc.) or copy it into your root folder.&lt;/p&gt;

&lt;p&gt;Open up &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;localhost:3000&lt;/a&gt; to verify that your Nest app is running with Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add docker-compose with PostgreSQL
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; allows you to define and run multiple Docker container together.Here you are setting up a Docker compose file for the Nest application and a PostgreSQL database.&lt;/p&gt;

&lt;p&gt;Create the Docker compose file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add following services to &lt;code&gt;docker.compose.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nest-api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nest-api&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3000:3000&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:12&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres:/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nest-db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first service &lt;strong&gt;nest-api&lt;/strong&gt; is building the Docker image based on your &lt;code&gt;Dockerfile&lt;/code&gt; for your Nest app with Prisma. The second service is creating a &lt;strong&gt;postgres&lt;/strong&gt; database using the &lt;code&gt;postgres&lt;/code&gt; Docker image in version &lt;code&gt;12&lt;/code&gt;. For the Postgres image set &lt;code&gt;POSTGRESQL_USER&lt;/code&gt;, &lt;code&gt;POSTGRESQL_PASSWORD&lt;/code&gt; and &lt;code&gt;POSTGRES_DB&lt;/code&gt; environment variables in a &lt;code&gt;.env&lt;/code&gt; file next to your &lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POSTGRESQL_USER=prisma
POSTGRESQL_PASSWORD=topsecret
POSTGRES_DB=food
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To connect to the PostgreSQL database Docker image &lt;a href="https://www.prisma.io/docs/reference/database-connectors/postgresql" rel="noopener noreferrer"&gt;configure&lt;/a&gt; the &lt;code&gt;DATABASE_URL&lt;/code&gt; in your &lt;code&gt;.env&lt;/code&gt; file. Fill in your values into the Postgres connection url format&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgresql://USER:PASSWORD@HOST:PORT/DB?schema=NAME&amp;amp;sslmode=prefer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example add the following variable to the &lt;code&gt;.env&lt;/code&gt; file. The &lt;code&gt;HOST&lt;/code&gt; is when connecting from another Docker image either the service name or the container name - both &lt;code&gt;postgres&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgresql://prisma:topsecret@postgres:5432/food?schema=food&amp;amp;sslmode=prefer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time 🕙 to start your Nest app and Postgres Docker image. Make sure the ports &lt;code&gt;3000&lt;/code&gt; and &lt;code&gt;5432&lt;/code&gt; are not in use already.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;span class="c"&gt;# or detached&lt;/span&gt;
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should have the following two docker containers started&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Fdockerizing-nestjs-with-prisma-and-postgresql%2Foptimized%2Fdocker-compose.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Fdockerizing-nestjs-with-prisma-and-postgresql%2Foptimized%2Fdocker-compose.png" alt="Docker container started by docker-compose"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open again &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;localhost:3000&lt;/a&gt; to verify that your Nest app is running with Docker. Also verify that your endpoints using the Prisma Client have access to the Postgres DB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prisma Migrate Postgres Docker Container
&lt;/h2&gt;

&lt;p&gt;Replace the host &lt;code&gt;postgres&lt;/code&gt; with &lt;code&gt;localhost&lt;/code&gt; if you want to perform &lt;a href="https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-migrate" rel="noopener noreferrer"&gt;Prisma migrations&lt;/a&gt; locally of your Postgres Docker container. Update the &lt;code&gt;DATABASE_URL&lt;/code&gt; in &lt;code&gt;prisma/.env&lt;/code&gt; to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgresql://prisma:topsecret@localhost:5432/food?schema=food&amp;amp;sslmode=prefer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run &lt;code&gt;npx prisma migrate save --experimental&lt;/code&gt; and &lt;code&gt;npx prisma migrate save --experimental&lt;/code&gt; or even seed the database if you like.&lt;/p&gt;

&lt;p&gt;Perfect, now sit back and relax 🏝 and let Docker do the work for you.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>nestjs</category>
      <category>prisma</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Angular 10 with Tailwind CSS</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Mon, 13 Jul 2020 09:26:45 +0000</pubDate>
      <link>https://forem.com/notiz_dev/angular-10-with-tailwind-css-45j4</link>
      <guid>https://forem.com/notiz_dev/angular-10-with-tailwind-css-45j4</guid>
      <description>&lt;p&gt;Learn how to use utility-first CSS framework &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt; with &lt;a href="https://angular.io/"&gt;Angular&lt;/a&gt; using &lt;a href="https://github.com/manfredsteyer/ngx-build-plus"&gt;ngx-build-plus&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This guide works for both Tailwind CSS v1 and &lt;a href="https://blog.tailwindcss.com/tailwindcss-v2"&gt;v2&lt;/a&gt; and Angular v10 and &lt;a href="https://blog.angular.io/version-11-of-angular-now-available-74721b7952f7"&gt;v11&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular Project
&lt;/h2&gt;

&lt;p&gt;You need the &lt;a href="https://cli.angular.io/"&gt;Angular CLI&lt;/a&gt; to create a new Angular project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new app-name &lt;span class="nt"&gt;--style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;scss
&lt;span class="nb"&gt;cd &lt;/span&gt;app-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the instruction to manually configure Angular w/ Tailwind 🍬🍫🍪 or jump directly to the &lt;a href="https://dev.toblog/angular-10-with-tailwindcss#shortcut-aka-angular-schematics"&gt;shortcut&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Start by adding dependencies for Tailwind, Postcss and ngx-build-plus for angular.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss autoprefixer postcss postcss-import postcss-loader postcss-scss

ng add ngx-build-plus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;webpack.config.js&lt;/code&gt; in your root folder to configure Postcss with Tailwind.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;webpack.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;scss$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;postcssOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-import&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autoprefixer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open &lt;strong&gt;angular.json&lt;/strong&gt; file to apply the extra webpack config to generate Tailwind styles during &lt;code&gt;ng build&lt;/code&gt;, &lt;code&gt;ng serve&lt;/code&gt; and &lt;code&gt;ng test&lt;/code&gt;. If you used the schematics &lt;code&gt;ng add ngx-build-plus&lt;/code&gt; it automatically replaces &lt;code&gt;@angular-devkit/build-angular&lt;/code&gt; with &lt;code&gt;ngx-build-plus&lt;/code&gt; in your &lt;code&gt;angular.json&lt;/code&gt;. Additionally, add the &lt;code&gt;extraWebpackConfig&lt;/code&gt; to each build step. In the end your &lt;strong&gt;angular.json&lt;/strong&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;"architect":&lt;/span&gt; {
  "build": {
&lt;span class="gd"&gt;-   "builder": "@angular-devkit/build-angular:browser",
&lt;/span&gt;&lt;span class="gi"&gt;+   "builder": "ngx-build-plus:browser",
&lt;/span&gt;    "options": {
&lt;span class="gi"&gt;+     "extraWebpackConfig": "webpack.config.js",
&lt;/span&gt;      ...
    }
    ...
  },
  "serve": {
&lt;span class="gd"&gt;-   "builder": "@angular-devkit/build-angular:dev-server",
&lt;/span&gt;&lt;span class="gi"&gt;+   "builder": "ngx-build-plus:dev-server",
&lt;/span&gt;    "options": {
&lt;span class="gi"&gt;+     "extraWebpackConfig": "webpack.config.js",
&lt;/span&gt;      ...
    }
    ...
  },
  "test": {
&lt;span class="gd"&gt;-   "builder": "@angular-devkit/build-angular:karma",
&lt;/span&gt;&lt;span class="gi"&gt;+   "builder": "ngx-build-plus:karma",
&lt;/span&gt;    "options": {
&lt;span class="gi"&gt;+     "extraWebpackConfig": "webpack.config.js",
&lt;/span&gt;      ...
    }
    ...
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect, now it's time to generate the Tailwind config &lt;code&gt;npx tailwindcss init&lt;/code&gt; or for full config &lt;code&gt;npx tailwindcss init --full&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Almost there. Add Tailwind base styles to your &lt;code&gt;src/styles.scss&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/base'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/components'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/utilities'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go ahead serve your app, you are ready to style your Angular app with Tailwind utility classes.&lt;/p&gt;

&lt;p&gt;… wait a moment, we need to purge the unused CSS styles from Tailwind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove unused CSS Styles
&lt;/h2&gt;

&lt;p&gt;We can use the new &lt;a href="https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css"&gt;purge&lt;/a&gt; option in &lt;code&gt;tailwind.config.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/ **/*.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/** /*.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unused styles are removed by Tailwind when you run your build with &lt;code&gt;NODE_ENV&lt;/code&gt; set to &lt;code&gt;production&lt;/code&gt;. Add &lt;code&gt;"build:prod": "NODE_ENV=production ng build --prod",&lt;/code&gt; to your scripts in &lt;code&gt;package.json&lt;/code&gt;. Now run &lt;code&gt;npm run build:prod&lt;/code&gt; to get a production build with only used Tailwind styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS instead of SCSS
&lt;/h2&gt;

&lt;p&gt;Using CSS instead of SCSS? No problem. You don't need to install &lt;code&gt;postcss-scss&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss autoprefixer postcss postcss-import postcss-loader 

ng add ngx-build-plus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update also your &lt;code&gt;webpack.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;css$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;postcssOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-import&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autoprefixer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally add Tailwind base styles to &lt;code&gt;src/styles.css&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/base'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/components'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'tailwindcss/utilities'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Shortcut aka Angular Schematics
&lt;/h2&gt;

&lt;p&gt;If you also think the steps above are tedious … Don't look any further.&lt;/p&gt;

&lt;p&gt;Angular Schematics 💪 to the rescue. &lt;a href="https://notiz.dev/authors/gary-grossgarten"&gt;Gary&lt;/a&gt; created a &lt;a href="https://github.com/notiz-dev/ngx-tailwind"&gt;schematic&lt;/a&gt; to add Tailwind to an Angular project. Simply run the schematic to automatically configure Tailwind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng add ngx-tailwind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use Tailwind CSS utility classes
&lt;/h2&gt;

&lt;p&gt;Now go crazy with Tailwind's CSS utility classes in your Angular app.&lt;/p&gt;

&lt;p&gt;Add them to your HTML template &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;[class.hover:...]="true"&lt;/code&gt; or use them with &lt;code&gt;ngClass&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block bg-red-500 rounded-full px-3 py-1 text-sm font-semibold text-white"&lt;/span&gt; &lt;span class="na"&gt;[class.hover:bg-red-700]=&lt;/span&gt;&lt;span class="s"&gt;"hoverMe"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  #angular
&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add it to your stylesheet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;inline-block&lt;/span&gt; &lt;span class="err"&gt;bg-red-500&lt;/span&gt; &lt;span class="err"&gt;rounded-full&lt;/span&gt; &lt;span class="err"&gt;px-3&lt;/span&gt; &lt;span class="err"&gt;py-1&lt;/span&gt; &lt;span class="err"&gt;text-sm&lt;/span&gt; &lt;span class="err"&gt;font-semibold&lt;/span&gt; &lt;span class="err"&gt;text-white;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;bg-red-700;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : @apply is not compiled when using it in an Angular library due to &lt;a href="https://github.com/ng-packagr/ng-packagr/issues/1471"&gt;missing support for postcss&lt;/a&gt; of ng-packagr&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or use &lt;code&gt;@HostBinding&lt;/code&gt; in your &lt;code&gt;*.ts&lt;/code&gt; files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostBinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-red-500 px-4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostBinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class.hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isHidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following snippet to your &lt;code&gt;src/app.component.html&lt;/code&gt; to see if Tailwind styles the following card. (Don't worry about the picture its random)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--100SXv1r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/angular-10-with-tailwindcss/optimized/angular-tailwind-card.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--100SXv1r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/angular-10-with-tailwindcss/optimized/angular-tailwind-card.png" alt="Angular Tailwind Card"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-sm mx-auto mt-10 rounded overflow-hidden shadow-lg space-y-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-64 w-full object-cover object-center"&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://source.unsplash.com/random"&lt;/span&gt;
    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Random unsplash photo"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-bold text-xl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Angular w/ Tailwind&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-700 text-base"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus
      quia, nulla! Maiores et perferendis eaque, exercitationem praesentium
      nihil.
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-6 pb-4 space-x-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://angular.io"&lt;/span&gt;
      &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;
      &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noreferrer"&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block bg-red-500 rounded-full px-3 py-1 text-sm font-semibold text-white hover:bg-red-700"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      #angular
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://tailwindcss.com"&lt;/span&gt;
      &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;
      &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noreferrer"&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block bg-indigo-500 rounded-full px-3 py-1 text-sm font-semibold text-white hover:bg-indigo-700"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      #tailwind
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://notiz.dev"&lt;/span&gt;
      &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;
      &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noreferrer"&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 hover:bg-blue-400"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      #notiz
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next post you will create an Angular component for a floating form field based on my last post &lt;a href="https://notiz.dev/blog/floating-form-field-with-tailwindcss"&gt;Floating Form Field with Tailwind CSS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Upgrading from Tailwind CSS v1 to v2
&lt;/h3&gt;

&lt;p&gt;To upgrade you project from &lt;a href="https://tailwindcss.com/docs/upgrading-to-v2"&gt;Tailwind CSS v1.x to v2.0&lt;/a&gt; run the following install command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss@latest postcss@latest autoprefixer@latest postcss-import@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the full &lt;a href="https://tailwindcss.com/docs/upgrading-to-v2"&gt;Upgrade Guide&lt;/a&gt; to update your custom &lt;code&gt;tailwind.config.js&lt;/code&gt; (e.g. &lt;a href="https://tailwindcss.com/docs/upgrading-to-v2#configure-your-color-palette-explicitly"&gt;your color palette&lt;/a&gt;) and replace removed classes from your template (e.g. &lt;a href="https://tailwindcss.com/docs/upgrading-to-v2#replace-shadow-outline-and-shadow-xs-with-ring-utilities"&gt;shadow-outline and shadow-xs&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Update postcss-loader from 3.x to 4.x
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/webpack-contrib/postcss-loader"&gt;postcss-loader&lt;/a&gt; has new &lt;a href="https://github.com/webpack-contrib/postcss-loader/blob/master/CHANGELOG.md#-breaking-changes"&gt;breaking changes&lt;/a&gt; when updating from version 3.x to 4.x. Huge thanks to &lt;a href="https://github.com/notiz-dev/notiz/issues/111#issuecomment-689249664"&gt;@phileagleson&lt;/a&gt; 👏 who pointed out that options for Postcss have moved to the &lt;code&gt;postcssOptions&lt;/code&gt;. Therefore, update your &lt;code&gt;webpack.config.js&lt;/code&gt; as follows when updating to &lt;code&gt;postcss-loader@4.x&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;  module: {
    rules: [
      {
        test: /\.scss$/,
        loader: 'postcss-loader',
        options: {
&lt;span class="gd"&gt;-          ident: 'postcss',
-          syntax: 'postcss-scss',
-          plugins: () =&amp;gt; [
-            require('postcss-import'),
-            require('tailwindcss'),
-            require('autoprefixer'),
-          ],
&lt;/span&gt;&lt;span class="gi"&gt;+          postcssOptions: {
+           ident: 'postcss',
+            syntax: 'postcss-scss',
+            plugins: [
+              require('postcss-import'),
+              require('tailwindcss'),
+              require('autoprefixer'),
&lt;/span&gt;            ],
          },
        },
      },
    ],
  },
&lt;span class="err"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All &lt;code&gt;webpack.config.js&lt;/code&gt; examples are updated to use the new &lt;code&gt;postcssOptions&lt;/code&gt; for &lt;code&gt;postcss-loader@4.x&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tailwindcss</category>
      <category>css</category>
    </item>
    <item>
      <title>Floating Form Field with Tailwind CSS</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Thu, 28 May 2020 20:54:11 +0000</pubDate>
      <link>https://forem.com/notiz_dev/floating-form-field-with-tailwind-css-47n3</link>
      <guid>https://forem.com/notiz_dev/floating-form-field-with-tailwind-css-47n3</guid>
      <description>&lt;p&gt;In the following lesson you will learn how to use &lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; utility-first approach to create a floating form field known from &lt;a href="https://material.io/components/text-fields" rel="noopener noreferrer"&gt;Material Design&lt;/a&gt;. This is inspired by the awesome video from &lt;a href="https://www.youtube.com/watch?v=yrrw6KdGuxc" rel="noopener noreferrer"&gt;fireship.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Ffloating-form.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Ffloating-form.gif" alt="Floating Form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can follow along in a new project or get the &lt;a href="https://github.com/notiz-dev/floating-form-field-tailwindcss" rel="noopener noreferrer"&gt;source code&lt;/a&gt; from GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Let's start in an empty directory and setup up a default &lt;code&gt;package.json&lt;/code&gt; file using &lt;code&gt;npm init -y&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Tailwind CSS
&lt;/h3&gt;

&lt;p&gt;Setup &lt;a href="https://tailwindcss.com/docs/installation/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; by installing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -D tailwindcss postcss-cli autoprefixer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate a Tailwind config file as you will &lt;a href="https://tailwindcss.com/docs/configuration/" rel="noopener noreferrer"&gt;customize&lt;/a&gt; Tailwind for the floating form field later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx tailwindcss init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;postcss.config.js&lt;/code&gt; file requiring &lt;code&gt;tailwindcss&lt;/code&gt; and &lt;code&gt;autoprefixer&lt;/code&gt; as a plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  plugins: [
    require("tailwindcss"),
    require("autoprefixer")
  ]
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create your tailwind styles under &lt;code&gt;css/tailwind.css&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@tailwind base;
@tailwind components;
@tailwind utilities;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add those scripts to your &lt;code&gt;package.json&lt;/code&gt; one to build and the other to watch changes made to the &lt;code&gt;tailwind.css&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "build": "postcss css/tailwind.css -o public/build/tailwind.css",
  "dev": "postcss css/tailwind.css -o public/build/tailwind.css -w"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup HTML
&lt;/h3&gt;

&lt;p&gt;Start with the following simple HTML layout - create an &lt;code&gt;index.html&lt;/code&gt; in the &lt;strong&gt;public&lt;/strong&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8" /&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&amp;gt;
    &amp;lt;title&amp;gt;FFFwT&amp;lt;/title&amp;gt;

    &amp;lt;link rel="stylesheet" href="build/tailwind.css" /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body class="antialiased p-10"&amp;gt;
    &amp;lt;form&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;input type="text" name="username" placeholder=" " /&amp;gt;
        &amp;lt;label for="username"&amp;gt;Username&amp;lt;/label&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;a href="https://www.npmjs.com/package/live-server" rel="noopener noreferrer"&gt;live-server&lt;/a&gt; to start a dev-server with live reload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;live-server public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you have your starting point - an input and a label.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F1-input-and-label.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F1-input-and-label.png" alt="Input and Label"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's add styles to create a floating form field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Floating Form Field
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Focus Border
&lt;/h3&gt;

&lt;p&gt;Start by adding a bottom border to the &lt;code&gt;div&lt;/code&gt; using &lt;code&gt;border-b-{width}&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="border-b-2"&amp;gt;
  &amp;lt;input type="text" name="username" placeholder=" " /&amp;gt;
  &amp;lt;label for="username"&amp;gt;Username&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F2-border.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F2-border.png" alt="Border bottom"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To change the border color when the input is focused use the pseudo-class &lt;code&gt;focus-within&lt;/code&gt;. Enable the &lt;a href="https://tailwindcss.com/docs/pseudo-class-variants/#focus-within" rel="noopener noreferrer"&gt;focus-within variant&lt;/a&gt; in Tailwind for &lt;code&gt;borderColor&lt;/code&gt; by adding it in the &lt;code&gt;tailwind.config.js&lt;/code&gt; under the variants section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variants: {
  borderColor: ['responsive', 'hover', 'focus', 'focus-within'],
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add &lt;code&gt;focus-within:border-blue-500&lt;/code&gt; to change the border color on focus&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="my-4 border-b-2 focus-within:border-blue-500"&amp;gt;
  &amp;lt;input type="text" name="username" placeholder=" " /&amp;gt;
  &amp;lt;label for="username"&amp;gt;Username&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F3-focus-border-color.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F3-focus-border-color.png" alt="Change border color on focus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Floating Label
&lt;/h3&gt;

&lt;p&gt;Begin with changing the position of the &lt;code&gt;div&lt;/code&gt; to &lt;code&gt;relative&lt;/code&gt; so that you can use &lt;code&gt;top&lt;/code&gt; to control the position of the &lt;code&gt;label&lt;/code&gt;. Add &lt;code&gt;class="absolute top-0"&lt;/code&gt; to the &lt;code&gt;label&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;input&lt;/code&gt; is an inline element, add the Tailwind &lt;code&gt;block&lt;/code&gt; class to change it to a block element. Also set the input width to 100% with &lt;code&gt;w-full&lt;/code&gt; to tap the input on the whole form field. Additionally, add &lt;code&gt;appearance-none&lt;/code&gt; and &lt;code&gt;focus:outline-none&lt;/code&gt; to the input to remove browser specific styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="relative my-4 border-b-2 focus-within:border-blue-500"&amp;gt;
  &amp;lt;input type="text" name="username" placeholder=" " class="block w-full appearance-none focus:outline-none" /&amp;gt;
  &amp;lt;label for="username" class="absolute top-0"&amp;gt;Username&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently the label is covering the input field and preventing you from focusing the input 🙈.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F4-label-covers-input.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F4-label-covers-input.png" alt="Label is covering up the input"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's change the &lt;code&gt;z-index&lt;/code&gt; of the label to be behind the input field by setting it to &lt;code&gt;z-index: -1&lt;/code&gt;. Extend the Tailwind theme to generate a negative z-index for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;theme: {
  extend: {
    zIndex: {
      "-1": "-1",
    },
  },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;-z-1&lt;/code&gt; class to the label, now the label is not visible anymore. Add &lt;code&gt;bg-transparent&lt;/code&gt; to the &lt;code&gt;input&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="relative my-4 border-b-2 focus-within:border-blue-500"&amp;gt;
  &amp;lt;input type="text" name="username" placeholder=" " class="block w-full appearance-none focus:outline-none bg-transparent" /&amp;gt;
  &amp;lt;label for="username" class="absolute top-0 -z-1"&amp;gt;Username&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The label is visible again and the input field can be focused by taping on the label too 🐵.&lt;/p&gt;

&lt;p&gt;Next make the label float above the input, again, using the pseudo-class &lt;code&gt;focus-within&lt;/code&gt;. Open your &lt;code&gt;tailwind.css&lt;/code&gt; and add the following CSS selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input:focus-within ~ label {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now use Tailwind's &lt;a href="https://tailwindcss.com/docs/functions-and-directives/#apply" rel="noopener noreferrer"&gt;@apply&lt;/a&gt; to transform, scale and change the label text color on input focus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input:focus-within ~ label {
 @apply transform scale-75 -translate-y-6 text-blue-500;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also add &lt;code&gt;duration-300&lt;/code&gt; to your label class to control the labels transition duration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F5-floating-label-on-focus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F5-floating-label-on-focus.png" alt="Floating label on focus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome the label is floating 🎈, however, it stops floating when you remove the focus from the input 😞. The label should float if the input field has some content. Target the &lt;code&gt;placeholder&lt;/code&gt;, if its not shown &lt;code&gt;input:not(:placeholder-shown) ~ label&lt;/code&gt; thus the input has content and the label should float.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input:focus-within ~ label,
input:not(:placeholder-shown) ~ label {
  @apply transform scale-75 -translate-y-6;
}

input:focus-within ~ label {
  @apply text-blue-500;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F6-floating-label-without-focus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F6-floating-label-without-focus.png" alt="Floating label without focus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah 🤩, the label floats on focus and if the input has content. It seems the label is not aligned with the input field. Set &lt;code&gt;transform-origin&lt;/code&gt; on the label to 0%. Let Tailwind generate it for you, open &lt;code&gt;tailwind.config.js&lt;/code&gt; and add it to the theme section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;theme: {
  extend: {
    transformOrigin: {
      "0": "0%",
    },
    zIndex: {
      "-1": "-1",
    },
  },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add &lt;code&gt;origin-0&lt;/code&gt; to the label and finally you have your own floating form field made with Tailwind CSS 😍🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F7-floating-form-field.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F7-floating-form-field.png" alt="Floating Form Field"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a form
&lt;/h3&gt;

&lt;p&gt;Let's build a form field by duplicating the floating form field or by extracting the styles with &lt;a href="https://tailwindcss.com/docs/functions-and-directives/#apply" rel="noopener noreferrer"&gt;@apply&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;space-y-{size}&lt;/code&gt; to the form to add margin between your input fields. You could also wrap it with a card.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;form class="max-w-sm mx-auto rounded-lg shadow-xl overflow-hidden p-6 space-y-10"&amp;gt;
  &amp;lt;h2 class="text-2xl font-bold text-center"&amp;gt;Login&amp;lt;/h2&amp;gt;
  &amp;lt;div class="relative border-b-2 focus-within:border-blue-500"&amp;gt;
    &amp;lt;input type="text" name="username" placeholder=" " class="block w-full appearance-none focus:outline-none bg-transparent" /&amp;gt;
    &amp;lt;label for="username" class="absolute top-0 -z-1 duration-300 origin-0"&amp;gt;Username&amp;lt;/label&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class="relative border-b-2 focus-within:border-blue-500"&amp;gt;
    &amp;lt;input type="text" name="email" placeholder=" " class="block w-full appearance-none focus:outline-none bg-transparent" /&amp;gt;
    &amp;lt;label for="email" class="absolute top-0 -z-1 duration-300 origin-0"&amp;gt;Email&amp;lt;/label&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class="relative border-b-2 focus-within:border-blue-500"&amp;gt;
    &amp;lt;input type="password" name="password" placeholder=" " class="block w-full appearance-none focus:outline-none bg-transparent" /&amp;gt;
    &amp;lt;label for="password" class="absolute top-0 -z-1 duration-300 origin-0"&amp;gt;Password&amp;lt;/label&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you have your first form using the floating form field&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Ffloating-form.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Ffloating-form.gif" alt="Floating form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, try out another style - Outline Form Field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outline Form Field
&lt;/h2&gt;

&lt;p&gt;In this style the form field has an outline all around and the label floats into the outline. Reuse the floating form field and change &lt;code&gt;border-b-2&lt;/code&gt; to &lt;code&gt;border-2&lt;/code&gt; this gives you the outline.&lt;/p&gt;

&lt;p&gt;Add padding &lt;code&gt;p-4&lt;/code&gt; to create space inside the outline and increase the font size &lt;code&gt;text-lg&lt;/code&gt; of the input and the label.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="relative border-2 focus-within:border-blue-500"&amp;gt;
  &amp;lt;input type="text" name="username" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" /&amp;gt;
  &amp;lt;label for="username" class="absolute top-0 p-4 text-lg -z-1 duration-300 origin-0"&amp;gt;Username&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at our outline form field&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F8-outline-form-field.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F8-outline-form-field.png" alt="Outline form field"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Focus the input and you notice the label is &lt;strong&gt;not&lt;/strong&gt; covering up the outline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F9-outline-floating-label-below.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F9-outline-floating-label-below.png" alt="Floating label not covering the outline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make the label cover up the outline customize the floating CSS applied to the outline form field. Duplicate the custom CSS in your &lt;code&gt;tailwind.css&lt;/code&gt; and add &lt;code&gt;.outline&lt;/code&gt; class to both selectors. Add &lt;code&gt;outline&lt;/code&gt; class to the &lt;code&gt;div&lt;/code&gt; around your input and label.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.outline input:focus-within ~ label,
.outline input:not(:placeholder-shown) ~ label {
  @apply transform scale-75 -translate-y-6;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update a few styles on the label to remove the top and bottom padding - &lt;code&gt;py-0&lt;/code&gt;. Also reduce the left and right padding to &lt;code&gt;px-1&lt;/code&gt;, add margin left &lt;code&gt;ml-3&lt;/code&gt; to align it with the input. Reset the z-index to &lt;code&gt;z-0&lt;/code&gt; that the label is above the outline. Finally, reduce the translate y value until the label is perfectly matching the outline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.outline input:focus-within ~ label,
.outline input:not(:placeholder-shown) ~ label {
  @apply transform scale-75 -translate-y-4 z-0 ml-3 px-1 py-0;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect the label is exactly on the outline, but the line is still visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F10-outline-floating-label-with-line-through.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foptimized%2F10-outline-floating-label-with-line-through.png" alt="Floating label covers outline with line through"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To hide the outline set the background of the label to the same background of the container where you place the form field. Add &lt;code&gt;bg-white&lt;/code&gt; to the label, this should do the trick.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="outline relative border-2 focus-within:border-blue-500"&amp;gt;
  &amp;lt;input type="text" name="username" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" /&amp;gt;
  &amp;lt;label for="username" class="absolute top-0 text-lg bg-white p-4 -z-1 duration-300 origin-0"&amp;gt;Username&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have the outline form field working too. 👍&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a form
&lt;/h3&gt;

&lt;p&gt;Now create a form using the outline style.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form class="max-w-sm mx-auto rounded-lg shadow-xl overflow-hidden p-6 space-y-10"&amp;gt;
  &amp;lt;h2 class="text-2xl font-bold text-center"&amp;gt;Login&amp;lt;/h2&amp;gt;
  &amp;lt;div class="outline relative border-2 focus-within:border-blue-500"&amp;gt;
    &amp;lt;input type="text" name="username" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" /&amp;gt;
    &amp;lt;label for="username" class="absolute top-0 text-lg bg-white p-4 -z-1 duration-300 origin-0"&amp;gt;Username&amp;lt;/label&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class="outline relative border-2 focus-within:border-blue-500"&amp;gt;
    &amp;lt;input type="email" name="email" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" /&amp;gt;
    &amp;lt;label for="email" class="absolute top-0 text-lg bg-white p-4 -z-1 duration-300 origin-0"&amp;gt;Email&amp;lt;/label&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class="outline relative border-2 focus-within:border-blue-500"&amp;gt;
    &amp;lt;input type="password" name="password" placeholder=" " class="block p-4 w-full text-lg appearance-none focus:outline-none bg-transparent" /&amp;gt;
    &amp;lt;label for="password" class="absolute top-0 text-lg bg-white p-4 -z-1 duration-300 origin-0"&amp;gt;Password&amp;lt;/label&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foutline-form.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnotiz.dev%2Fassets%2Fimg%2Fblog%2Ffloating-form-field-with-tailwindcss%2Foutline-form.gif" alt="Outline Form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you followed along, create and customize your own floating form field w/ Tailwind CSS. Send me your own design on Twitter &lt;a href="https://twitter.com/mrcjln" rel="noopener noreferrer"&gt;@mrcjln&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Migrate Git Repository to Git Large File Storage (LFS)</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Mon, 27 Apr 2020 16:30:01 +0000</pubDate>
      <link>https://forem.com/notiz_dev/migrate-git-repository-to-git-large-file-storage-lfs-4fc2</link>
      <guid>https://forem.com/notiz_dev/migrate-git-repository-to-git-large-file-storage-lfs-4fc2</guid>
      <description>&lt;p&gt;If you ever come across this error while pushing an existing repository or a large file to GitHub…&lt;/p&gt;

&lt;p&gt;&lt;code&gt;remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;… here is how to setup &lt;a href="https://git-lfs.github.com/"&gt;Git Large File Storage (LFS)&lt;/a&gt; and migrate your Git history.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Download and Install Git LFS extension
&lt;/h2&gt;

&lt;p&gt;Download and install the &lt;a href="https://git-lfs.github.com/"&gt;Git LFS&lt;/a&gt; extension, you can also install it using &lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;git-lfs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Setup Git LFS for your current user account
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git lfs &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Select files to be managed by Git LFS
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# track files by file type&lt;/span&gt;
git lfs track &lt;span class="s2"&gt;"*.zip"&lt;/span&gt;

&lt;span class="c"&gt;# track directories by path&lt;/span&gt;
git lfs track &lt;span class="s2"&gt;"assets/*"&lt;/span&gt;

&lt;span class="c"&gt;# track entire directory trees&lt;/span&gt;
git lfs track &lt;span class="s2"&gt;"assets/**/*"&lt;/span&gt;

&lt;span class="c"&gt;# track file by path&lt;/span&gt;
git lfs track &lt;span class="s2"&gt;"path/to/file"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git lfs track&lt;/code&gt; will add the files tracked by Git LFS to &lt;code&gt;.gitattributes&lt;/code&gt;. It is important to add &lt;code&gt;.gitattributes&lt;/code&gt; to Git.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git add .gitattributes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : Tracking files are &lt;strong&gt;not&lt;/strong&gt; automatically converting these files from your Git history or other branches.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Migrate Git History
&lt;/h2&gt;

&lt;p&gt;If you have existing files in your Git history or in other branches you need to migrate those files to be tracked by Git LFS as well. Git LFS provides a command &lt;a href="https://github.com/git-lfs/git-lfs/blob/master/docs/man/git-lfs-migrate.1.ronn?utm_source=gitlfs_site&amp;amp;utm_medium=doc_man_migrate_link&amp;amp;utm_campaign=gitlfs"&gt;git lfs migrate&lt;/a&gt; with various options depending on your use case.&lt;/p&gt;

&lt;p&gt;Before performing your migration you can perform a dry run with &lt;code&gt;git lfs migrate info [options]&lt;/code&gt;. Use the option &lt;code&gt;--everything&lt;/code&gt; to perform a migration in every branch. If you only want to migrate files you added before with &lt;code&gt;git lfs track&lt;/code&gt; you will add those with the &lt;code&gt;--include="*.zip,src/assets"&lt;/code&gt; flag comma separated.&lt;/p&gt;

&lt;p&gt;Here is an example which performs a migration for all Zip-files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# dry run of your migration&lt;/span&gt;
git lfs migrate info &lt;span class="nt"&gt;--everything&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.zip"&lt;/span&gt;

&lt;span class="c"&gt;# perform migration&lt;/span&gt;
git lfs migrate import &lt;span class="nt"&gt;--everything&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.zip"&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now go ahead and push your repository to GitHub. If successfully pushed and setup GitHub displays the following tag for each file tracked by Git LFS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jNJmDGZV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/migrate-git-repo-to-git-lfs/optimized/stored-with-git-lfs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jNJmDGZV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/migrate-git-repo-to-git-lfs/optimized/stored-with-git-lfs.png" alt="Stored with Git LFS on GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
    </item>
    <item>
      <title>How to manage multiple Java JDK versions on macOS X</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Thu, 16 Apr 2020 10:10:13 +0000</pubDate>
      <link>https://forem.com/notiz_dev/how-to-manage-multiple-java-jdk-versions-on-macos-x-41mi</link>
      <guid>https://forem.com/notiz_dev/how-to-manage-multiple-java-jdk-versions-on-macos-x-41mi</guid>
      <description>&lt;p&gt;Here is a quick tip on how to install multiple &lt;a href="https://www.oracle.com/java/technologies/javase-downloads.html#javasejdk"&gt;Java JDK&lt;/a&gt; versions (8, …, 11, …, 14 etc.) on macOS X and how to switch between them for your applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Java JDK via Homebrew
&lt;/h2&gt;

&lt;p&gt;Install multiple Java JDK versions using &lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt;. To install Homebrew run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;/bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Homebrew/install/master/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now install the Java JDK version &lt;strong&gt;11&lt;/strong&gt; or above using &lt;code&gt;brew cask&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;brew cask &lt;span class="nb"&gt;install &lt;/span&gt;java&amp;lt;version&amp;gt;

&lt;span class="c"&gt;# latest version&lt;/span&gt;
brew cask &lt;span class="nb"&gt;install &lt;/span&gt;java

&lt;span class="c"&gt;# LTS 11&lt;/span&gt;
brew cask &lt;span class="nb"&gt;install &lt;/span&gt;java11
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; JDK versions prior &lt;strong&gt;11&lt;/strong&gt; ( &lt;strong&gt;8&lt;/strong&gt; , &lt;strong&gt;9&lt;/strong&gt; and &lt;strong&gt;10&lt;/strong&gt; ) are no longer supported.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://adoptopenjdk.net/"&gt;AdoptOpenJDK&lt;/a&gt; provides older Java versions. To install the Java JDKs from AdoptOpenJDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install from third party repository&lt;/span&gt;
brew tap adoptopenjdk/openjdk

brew cask &lt;span class="nb"&gt;install &lt;/span&gt;adoptopenjdk&amp;lt;version&amp;gt;

&lt;span class="c"&gt;# Java 8&lt;/span&gt;
brew cask &lt;span class="nb"&gt;install &lt;/span&gt;adoptopenjdk8

&lt;span class="c"&gt;# Java 9&lt;/span&gt;
brew cask &lt;span class="nb"&gt;install &lt;/span&gt;adoptopenjdk9

&lt;span class="c"&gt;# Java 10&lt;/span&gt;
brew cask &lt;span class="nb"&gt;install &lt;/span&gt;adoptopenjdk10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Switch Java JDK via alias
&lt;/h2&gt;

&lt;p&gt;Setup your &lt;code&gt;JAVA_HOME&lt;/code&gt; path in your &lt;code&gt;.zshrc&lt;/code&gt; or &lt;code&gt;.bash_profile&lt;/code&gt; for your primary Java version and add an export for each installed Java version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;/usr/libexec/java_home &lt;span class="nt"&gt;-v14&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_8_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;/usr/libexec/java_home &lt;span class="nt"&gt;-v1&lt;/span&gt;.8&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_11_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;/usr/libexec/java_home &lt;span class="nt"&gt;-v11&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_14_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;/usr/libexec/java_home &lt;span class="nt"&gt;-v14&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To check the default Java version and installation path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;-version&lt;/span&gt; &lt;span class="c"&gt;# 14&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add an alias to your &lt;code&gt;.zshrc&lt;/code&gt; or &lt;code&gt;.bash_profile&lt;/code&gt; for each installed Java version. The alias exports &lt;code&gt;JAVA_HOME&lt;/code&gt; with the selected &lt;code&gt;JAVA_VERSION_HOME&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;java8&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'export JAVA_HOME=$JAVA_8_HOME'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;java11&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'export JAVA_HOME=$JAVA_11_HOME'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;java14&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'export JAVA_HOME=$JAVA_14_HOME'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, to switch between the Java versions, enter an alias &lt;code&gt;java8&lt;/code&gt; in your terminal. Execute &lt;code&gt;java -version&lt;/code&gt; to verify that you are now using the correct Java version.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : Alias &lt;strong&gt;only&lt;/strong&gt; changes the Java version in the used terminal instance&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>java</category>
      <category>webdev</category>
    </item>
    <item>
      <title>GraphQL Code-First Approach with NestJS 7</title>
      <dc:creator>Marc Stammerjohann</dc:creator>
      <pubDate>Tue, 31 Mar 2020 18:58:05 +0000</pubDate>
      <link>https://forem.com/notiz_dev/graphql-code-first-approach-with-nestjs-7-ahm</link>
      <guid>https://forem.com/notiz_dev/graphql-code-first-approach-with-nestjs-7-ahm</guid>
      <description>&lt;p&gt;Recently the release of &lt;a href="https://trilon.io/blog/announcing-nestjs-7-whats-new"&gt;NestJS 7&lt;/a&gt; was announced with amazing updates to the whole framework including the &lt;a href="https://docs.nestjs.com/graphql/quick-start"&gt;@nestjs/graphql&lt;/a&gt; ❤️ package.&lt;/p&gt;

&lt;p&gt;We create a &lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt; API using the &lt;code&gt;@nestjs/graphql&lt;/code&gt;. You will learn how to write the API with TypeScript using the &lt;strong&gt;code first&lt;/strong&gt; approach and the new &lt;a href="https://docs.nestjs.com/graphql/resolvers#cli-plugin"&gt;GraphQL plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this guide we are using &lt;a href="https://prisma.io"&gt;Prisma&lt;/a&gt; to easily access a database. You can follow this guide to setup a &lt;a href="https://notiz.dev/blog/how-to-connect-nestjs-with-prisma"&gt;Nest application with Prisma&lt;/a&gt; as Prisma is out of scope for this guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup GraphQL
&lt;/h2&gt;

&lt;p&gt;To start a GraphQL API install the following packages into your Nest application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--save&lt;/span&gt; @nestjs/graphql graphql-tools graphql

&lt;span class="c"&gt;# for Express&lt;/span&gt;
npm i &lt;span class="nt"&gt;--save&lt;/span&gt; apollo-server-express
&lt;span class="c"&gt;# for Fastify&lt;/span&gt;
npm i &lt;span class="nt"&gt;--save&lt;/span&gt; apollo-server-fastify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Import the &lt;code&gt;GraphQLModule&lt;/code&gt; into your &lt;code&gt;AppModule&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GraphQLModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;GraphQLModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;autoSchemaFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/schema.gql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;playground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To configure the GraphQL endpoint we use &lt;code&gt;GqlModuleOptions&lt;/code&gt; which are passed to the underlying GraphQL server. Here we are enabling the &lt;a href="https://docs.nestjs.com/graphql/quick-start#code-first"&gt;&lt;strong&gt;code first&lt;/strong&gt;&lt;/a&gt; approach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;autoSchemaFile&lt;/code&gt; enables the &lt;strong&gt;code first&lt;/strong&gt; approach to use TypeScript classes and decorators to generate the GraphQL schema.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;playground&lt;/code&gt; enables the &lt;a href="https://github.com/prisma-labs/graphql-playground"&gt;GraphQl Playground&lt;/a&gt;, an interactive IDE for your API documentation, available at &lt;a href="http://localhost:3000/graphql"&gt;http://localhost:3000/graphql&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;debug&lt;/code&gt; mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are two options for &lt;code&gt;autoSchemaFile&lt;/code&gt; providing a &lt;strong&gt;path&lt;/strong&gt; for the schema generation or &lt;code&gt;true&lt;/code&gt; for generating the schema in memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Code First approach
&lt;/h2&gt;

&lt;p&gt;A GraphQL schema contains many &lt;a href="https://graphql.org/learn/schema/"&gt;types&lt;/a&gt; and &lt;a href="https://graphql.org/learn/queries/"&gt;Queries&lt;/a&gt;. The schema grows in size and complexity for each new query, mutation and type. GraphQL &lt;a href="https://www.youtube.com/watch?v=OloBAdNCnyQ"&gt;&lt;strong&gt;Code First&lt;/strong&gt;&lt;/a&gt; enables us to automatically generate a GraphQL schema using TypeScript and decorators. This helps us focus on writing &lt;code&gt;.ts&lt;/code&gt; files and we don't need to write the GraphQL schema ourselves.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@nestjs/graphql&lt;/code&gt; provides all decorators to generate our schema. Here are a few decorators and there usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@ObjectType()&lt;/code&gt; generate class as &lt;a href="https://graphql.org/learn/schema/#type-system"&gt;Type&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Field()&lt;/code&gt; generate a class property as a &lt;a href="https://graphql.org/learn/schema/#object-types-and-fields"&gt;Field&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@InputType()&lt;/code&gt; generate class as &lt;a href="https://graphql.org/learn/schema/#input-types"&gt;Input&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Args&lt;/code&gt; generate method params as &lt;a href="https://graphql.org/learn/schema/#arguments"&gt;Arguments&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Query()&lt;/code&gt; generate method as &lt;a href="https://graphql.org/learn/schema/#the-query-and-mutation-types"&gt;Query&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Mutation()&lt;/code&gt; generate method as &lt;a href="https://graphql.org/learn/schema/#the-query-and-mutation-types"&gt;Mutation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@ResolveField&lt;/code&gt; resolve relationship property&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Graphql Type
&lt;/h3&gt;

&lt;p&gt;Start with creating your objects as a TypeScript &lt;code&gt;class&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's add &lt;a href="https://docs.nestjs.com/graphql/resolvers#code-first"&gt;decorators&lt;/a&gt; to expose this model in our GraphQL schema. Start adding &lt;code&gt;@ObjectType()&lt;/code&gt; to the TypeScript class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next we use the &lt;code&gt;@Field&lt;/code&gt; decorator on each class property providing additional information about the type and state (required or optional).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;registeredAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Hobby&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="nx"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The following GraphQL type is generated if this class is used in a resolver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;registeredAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Hobby&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@Field&lt;/code&gt; takes an optional type function (e.g. &lt;code&gt;type =&amp;gt; String&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Declare a field as an array using the bracket notation &lt;code&gt;[]&lt;/code&gt; in the type function (e.g. &lt;code&gt;type =&amp;gt; [Hobby]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Optional &lt;code&gt;FieldOptions&lt;/code&gt; object to change the generated schema&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: property name in the schema (&lt;code&gt;createdAt&lt;/code&gt; =&amp;gt; &lt;code&gt;registeredAt&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: adding a field description&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deprecationReason&lt;/code&gt;: adding a deprecation notice&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nullable&lt;/code&gt;: declare a field is required or optional&lt;/li&gt;
&lt;li&gt;Hide properties from the schema by omitting &lt;code&gt;@Field&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details head over to the NestJS &lt;a href="https://docs.nestjs.com/graphql/resolvers#object-types"&gt;docs&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;We have added a bit of boilerplate to our &lt;code&gt;User&lt;/code&gt; model and other models we will create. Nest provides a CLI plugin to reduce the boilerplate of our models. Check out the GraphQL plugin section on how to reduce the boilerplate.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL Resolver
&lt;/h3&gt;

&lt;p&gt;Great our models are in place! Now we use the Nest CLI to generate our resolvers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;nest generate resolver &amp;lt;name&amp;gt;

&lt;span class="c"&gt;# alias&lt;/span&gt;
nest g r &amp;lt;name&amp;gt;

&lt;span class="c"&gt;# User and Hobby&lt;/span&gt;
nest g r user
nest g r hobby
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our resolvers are added to the &lt;code&gt;providers&lt;/code&gt; array in the &lt;code&gt;app.module.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Resolver&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/user.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Resolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;UserResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Declare a &lt;code&gt;of&lt;/code&gt; function in the &lt;code&gt;@Resolver&lt;/code&gt; decorator (e.g. &lt;code&gt;@Resolver(of =&amp;gt; User)&lt;/code&gt;) this is used to provide a parent object in &lt;code&gt;@ResolveField&lt;/code&gt;. We will cover &lt;code&gt;@ResolveField&lt;/code&gt; in a bit.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;@Query&lt;/code&gt; to your resolvers to create new GraphQL queries in your schema. Let's create a query function returning all &lt;code&gt;users()&lt;/code&gt;. Use the bracket notation inside the decorator &lt;code&gt;@Query(returns =&amp;gt; [User])&lt;/code&gt; to declare an array return value.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Prisma is used in this example, but can be replaced easily with an ORM of your choice like &lt;a href="https://docs.nestjs.com/recipes/sql-typeorm"&gt;TypeORM&lt;/a&gt;, &lt;a href="https://docs.nestjs.com/recipes/mongodb"&gt;Mongoose&lt;/a&gt; or &lt;a href="https://docs.nestjs.com/recipes/sql-sequelize"&gt;Sequelize&lt;/a&gt;. See the full database setup in the &lt;a href="https://github.com/notiz-dev/nest-graphql-code-first"&gt;example repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Resolver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Query&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/user.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../prisma/prisma.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Resolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;UserResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;returns&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The above code generates the following query to our schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;User&lt;/code&gt; has a relation to many hobbies. To resolve the &lt;code&gt;hobbies&lt;/code&gt; property from a user, we make use of the &lt;code&gt;@ResolveField&lt;/code&gt; decorator. Add &lt;code&gt;@ResolveField&lt;/code&gt; to a function with the &lt;strong&gt;exact&lt;/strong&gt; same name of the property we want to resolve. Here we add a &lt;code&gt;hobbies()&lt;/code&gt; function and provide a &lt;code&gt;User&lt;/code&gt; object as the parent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Resolver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ResolveField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Parent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/user.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../prisma/prisma.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Resolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;UserResolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;returns&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ResolveField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hobby&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Use the parent object to query the relationship object from a database or another endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test GraphQL API
&lt;/h3&gt;

&lt;p&gt;Start your Nest application and navigate to the &lt;a href="http://localhost:3000/graphql"&gt;playground&lt;/a&gt;, it is available if &lt;code&gt;playground&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt; in the &lt;code&gt;GraphQLModule&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The playground shows us our GraphQL schema and the docs for our queries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o1vWhCI6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/graphql-code-first-with-nestjs-7/optimized/graphql-playground-schema.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o1vWhCI6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/graphql-code-first-with-nestjs-7/optimized/graphql-playground-schema.png" alt="Graphql Playground schema view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, we can "play" with queries inside the playground. Try out the &lt;strong&gt;autocomplete&lt;/strong&gt; feature in the playground to create your own queries based on your schema and queries.Let's query all users using the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AllUsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;registeredAt&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;updatedAt&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;hobbies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The response will look like this with a different data set. I prepared the database with a few dummy users and hobbies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1DKm5E8H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/graphql-code-first-with-nestjs-7/optimized/users-query.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1DKm5E8H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://notiz.dev/assets/img/blog/graphql-code-first-with-nestjs-7/optimized/users-query.png" alt="Users query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL plugin
&lt;/h2&gt;

&lt;p&gt;Nest 7 provides a new &lt;a href="https://docs.nestjs.com/graphql/resolvers#cli-plugin"&gt;GraphQL plugin&lt;/a&gt; to reduce the boilerplate of decorators for our &lt;strong&gt;models&lt;/strong&gt; , &lt;strong&gt;inputs&lt;/strong&gt; , &lt;strong&gt;args&lt;/strong&gt; and &lt;strong&gt;entity&lt;/strong&gt; files. Enable the plugin by adding &lt;code&gt;compilerOptions&lt;/code&gt; to &lt;code&gt;nest-cli.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": ["@nestjs/graphql/plugin"]
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The plugin automatically handles the decorators for the files with the suffix &lt;code&gt;['.input.ts', '.args.ts', '.entity.ts', '.model.ts']&lt;/code&gt;. If you like to use custom suffixes add those to the plugins option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"plugins": [
  {
    "name": "@nestjs/graphql/plugin",
    "options": {
      "typeFileNameSuffix": [".input.ts", ".model.ts"]
    }
  }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's clean up the boilerplate of our models. Before the plugin the models look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./hobby.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;registeredAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Hobby&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="nx"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After removing the extra boilerplate decorators the models looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HideField&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./hobby.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;registeredAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HideField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;hobbies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Hobby&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: Hiding properties from the schema requires the &lt;code&gt;@HideField&lt;/code&gt; decorator.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can add &lt;code&gt;@Field&lt;/code&gt; to any property to override the documentation and also the inferred type.For example &lt;code&gt;number&lt;/code&gt; is inferred as the GraphQL type &lt;code&gt;Float&lt;/code&gt; here we can use &lt;code&gt;@Field(type =&amp;gt; Int)&lt;/code&gt; to change this to an &lt;code&gt;Int&lt;/code&gt; type.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>graphql</category>
      <category>codefirst</category>
      <category>prisma</category>
    </item>
  </channel>
</rss>
