<?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>DEV Community: Jeffrey Bosch</title>
    <description>The latest articles on DEV Community by Jeffrey Bosch (@jefiozie).</description>
    <link>https://dev.to/jefiozie</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%2F150397%2Faadaec23-f08b-4b74-94c1-fbceea4458f9.jpeg</url>
      <title>DEV Community: Jeffrey Bosch</title>
      <link>https://dev.to/jefiozie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jefiozie"/>
    <language>en</language>
    <item>
      <title>Template validations made easy with Validointi</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Wed, 22 Feb 2023 11:50:36 +0000</pubDate>
      <link>https://dev.to/jefiozie/template-validations-made-easy-with-validointi-452c</link>
      <guid>https://dev.to/jefiozie/template-validations-made-easy-with-validointi-452c</guid>
      <description>&lt;p&gt;Have you ever created an Angular template form? Did you like the way you needed to do validations if it goes a bit further than required? Here comes a pain for many Angular developers and probably one of the reasons why a lot of Angular developers choose reactive forms over template forms.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TLDR&lt;/p&gt;

&lt;p&gt;The article explains how to use Validointi, a library that makes it easy to perform validations for Angular template forms beyond simple validations like "required". Validointi allows developers to use schema validation libraries like Vest, AJV, or Joi with Angular. The article covers basic concepts like Model, Validation Library, and Adapters and provides a step-by-step guide to hook up Validointi to an Angular template form.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article, we are going to have a look at an easy way to use template forms and go beyond a simple &lt;strong&gt;required&lt;/strong&gt; validation by using &lt;strong&gt;Validointi&lt;/strong&gt;. An easy to use library for Angular developers with a powerful setup to have a great developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Validointi
&lt;/h2&gt;

&lt;p&gt;To understand everything that we will cover in this article I will explain a couple of the basic concepts of Validointi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Model
&lt;/h3&gt;

&lt;p&gt;A model contains the information that we will use in our form. Below is an example of a simple model that represents a simple form. The model should always contain information and never any behavior.  In the example below we have a simple model with a definition for: name, email, password and a confirm password. This model will be used during the reset of the article as a baseline.&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="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'Your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;email:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'info@example.org'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;password:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'a-p-a-s-s-w-o-r-d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;confirmPassword:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'a-p-a-s-s-w-o-r-d'&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;h3&gt;
  
  
  Validation library
&lt;/h3&gt;

&lt;p&gt;By using Angular and more specifically Angular Forms, you will be provided by a couple of default validators like required, min, max and regex. The provided out of the box validators are directives that represent the HTML5 standard. When a form is getting more complex, we need to create custom validations. These validations can be plain functions with for example a regex, but we need to hook up the function to a directive to apply the validation to the template field.&lt;/p&gt;

&lt;p&gt;This is a lot of work for just a simple regex tests and most of the Angular devs don’t like this approach. In several other frontend frameworks, you can use schema validation libraries like Vest, AJV or Joi. In Angular this is not a “common” approach however, here is where Validointi comes into place. Validointi makes it possible to use these kinds of libraries easily with Angular. More on this later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adapters
&lt;/h3&gt;

&lt;p&gt;This is the last part of the concepts and probably this is the easiest one. Adapters do the heavy lifting to attach a library as Vest to Angular. Adapters are plain functions and can be created for a custom validation library. Today we will only use the Vest adapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooking up Validointi to Angular
&lt;/h2&gt;

&lt;p&gt;First let us set up the Angular template form, be aware we will use the new standalone API that has been introduced in Angular v15. In this article, we will not go into the exact details of template form.&lt;/p&gt;

&lt;p&gt;The form is pretty straightforward it is a registration form with a name, email, password and a confirm password. As we are using template forms we need to bind the &lt;code&gt;model&lt;/code&gt; with &lt;code&gt;ngModel&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;form&lt;/span&gt;  &lt;span class="na"&gt;(ngSubmit)=&lt;/span&gt;&lt;span class="s"&gt;"onSubmit($any(form))"&lt;/span&gt; &lt;span class="na"&gt;#form&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="na"&gt;ngForm&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Your name&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Fill in your name"&lt;/span&gt;
    &lt;span class="na"&gt;[(ngModel)]=&lt;/span&gt;&lt;span class="s"&gt;"model.name"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Your email&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Fill in your email"&lt;/span&gt;
    &lt;span class="na"&gt;[(ngModel)]=&lt;/span&gt;&lt;span class="s"&gt;"model.email"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Your password&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Fill in your password"&lt;/span&gt;
    &lt;span class="na"&gt;[(ngModel)]=&lt;/span&gt;&lt;span class="s"&gt;"model.password"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"confirmPassword"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Your confirm password&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"confirmPassword"&lt;/span&gt;
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Fill in your confirm password"&lt;/span&gt;
   &lt;span class="na"&gt;[(ngModel)]=&lt;/span&gt;&lt;span class="s"&gt;"model.confirmPassword"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt; &lt;span class="na"&gt;[disabled]=&lt;/span&gt;&lt;span class="s"&gt;"form.invalid"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we know how the HTML looks like have a look at the component code. The code is simple, we import the FormsModule, define a empty model and have a function that will be used when submitting the form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;inject&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;CommonModule&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/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;FormsModule&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/forms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Model&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="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;password&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;confirmPassword&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="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-template-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&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;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;CommonModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormsModule&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;./template-form.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;./template-form.component.css&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="nc"&gt;TemplateFormComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt; &lt;span class="o"&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="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;confirmPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&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;onSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;As the basics are set now we are going to hookup Validointi. First we import the directives that Validointi provides.&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;import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
&lt;/span&gt;&lt;span class="gi"&gt;+ import {
+  createVestAdapter,
+  ValidatorRegistryService,
+  ValidatorDirective,
+ } from '@validointi/core';
+ import { create, enforce, test } from 'vest';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;@Component({
  selector: 'app-template-form',
  standalone: true,
&lt;span class="gd"&gt;- imports: [CommonModule, FormsModule],
&lt;/span&gt;&lt;span class="gi"&gt;+ imports: [CommonModule, FormsModule, ValidatorDirective],
&lt;/span&gt;  templateUrl: './template-form.component.html',
  styleUrls: ['./template-form.component.css'],
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now make the validations for our model. As we explained earlier we will  use Vest in our example and that is why we have imported it.&lt;/p&gt;

&lt;p&gt;Vest works with a &lt;code&gt;suite&lt;/code&gt;, a suite is similar to a unit testing suite in Jest or Mocha. The following part will be recongnicable. In this suite we will validate the model with different rules, these rules can be sync or async.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Model&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="kr"&gt;string&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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;Name is required&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="nf"&gt;isNotBlank&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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;Name must be at least 3 characters long&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="nf"&gt;longerThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&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;Email is required&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="nf"&gt;isNotBlank&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not an valid email address&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="nf"&gt;matches&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;@&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;+$/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&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;Password is required&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="nf"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&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;Password is too short&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="nf"&gt;longerThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&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;Password is weak. maybe add a number&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="nf"&gt;matches&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;0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="nf"&gt;longerThanOrEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmPassword&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;Passwords do not match&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;enforce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirmPassword&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="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 only missing part is to hookup the test suite with the Angular form. Here is where validointi is coming into play. And, it is SOO easy, let’s see some code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;@Component({
  selector: 'app-template-form',
  standalone: true,
  imports: [CommonModule, FormsModule, ValidatorDirective],
  templateUrl: './template-form.component.html',
  styleUrls: ['./template-form.component.css'],
})
&lt;span class="p"&gt;export class TemplateFormComponent {
&lt;/span&gt;  model: Model = { name: '', email: '', confirmPassword: '', password: '' };
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+  #vr = inject(ValidatorRegistryService);
+  validate = this.#vr.registerValidator('form1', createVestAdapter(suite));
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  async onSubmit(data: any) {
&lt;span class="gi"&gt;+    const validationResult = await this.validate(data);
+    console.dir(validationResult);
&lt;/span&gt;  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- &amp;lt;form (ngSubmit)="onSubmit($any(form))" #form="ngForm"&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ &amp;lt;form validationId="form1" (ngSubmit)="onSubmit($any(form))" #form="ngForm"&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code we used registered our validation by using the &lt;code&gt;ValidatorRegistryService&lt;/code&gt; and added our Vest Suite to a validation function. When submitting a form we will validate the form with our test suite. In the HTML we will see the different error states when the test suite returns an invalid response.&lt;/p&gt;

&lt;p&gt;As we can see we have separated all of our test logic to the test suite and by using a simple service + adapter provided by Validointi we can use the test suite in our Angular template form.&lt;/p&gt;

&lt;p&gt;Below is a Stackblitz example of the complete project that you can use.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/validointi-sample-9vzrv7?embed=1&amp;amp;file=src/app/template-form/template-form.component.html" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;Personally, I’m a believer in keeping your components as thin as possible and deriving logic to services. In our example we added test logic, logic to hookup validointi etc. In our example it is still pretty simple but you can imagen that when there are more complex forms and validation rules this is not the best way.&lt;/p&gt;

&lt;p&gt;That is why I made the following changes to make our component thin again.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Removed all the test logic and moved it to the &lt;code&gt;data-service.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Removed the validointi logic and moved it to the &lt;code&gt;data-service.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Injected the &lt;code&gt;data-service.ts&lt;/code&gt; and use it in our &lt;code&gt;submit&lt;/code&gt; function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now our component is thin again and we have separated our “business logic” to a service (where it should be in the first place)&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/validointi-sample-wpuykb?embed=1&amp;amp;file=src/app/template-form/template-form.component.ts" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In summary, Validointi makes it easy to perform “complex” validations in Angular template forms without the hassle of making directive and validation functions. Validointi allows developers to use schema validation libraries like Vest, AJV, or Joi with Angular.&lt;/p&gt;

&lt;p&gt;Thank you for reading this article and if you are interested in Validointi, have a look at: &lt;a href="https://validointi.github.io/" rel="noopener noreferrer"&gt;https://validointi.github.io&lt;/a&gt;. I would like to thank &lt;a href="https://twitter.com/esosanderelias" rel="noopener noreferrer"&gt;Sander Elias&lt;/a&gt; for proof reading the article&lt;/p&gt;

&lt;p&gt;You can always hang out with me in one of the Dutch Angular Events or send me a DM at &lt;a class="mentioned-user" href="https://dev.to/jefiozie"&gt;@jefiozie&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>How to build a JamStack app with Angular Scully and Prisma</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Wed, 02 Feb 2022 19:29:35 +0000</pubDate>
      <link>https://dev.to/jefiozie/how-to-build-a-jamstack-app-with-angular-scully-and-prisma-4bkk</link>
      <guid>https://dev.to/jefiozie/how-to-build-a-jamstack-app-with-angular-scully-and-prisma-4bkk</guid>
      <description>&lt;h2&gt;
  
  
  TLDR;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;By using Angular  Scully and Prisma we have a powerful ecosystem where we can leverage full type safety and easily connect to a database that contains data that will be injected into the components by Scully. After the Scully process, we can upload our app to a CDN and have a fully working SEO-friendly Angular app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Prisma?
&lt;/h2&gt;

&lt;p&gt;Prisma is an open-source ORM, it consists of three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prisma Client&lt;/strong&gt;: Auto-generated and type-safe query builder for Node.js &amp;amp; TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prisma Migrate&lt;/strong&gt;: Migration system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prisma Studio&lt;/strong&gt;: GUI to view and edit data in your database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prisma Client can be used in &lt;em&gt;any&lt;/em&gt; Node.js (supported versions) environment.  The power of Prisma is that they have a good and powerful type safety solution when using it with Typescript, and by default supports a set of Easy to use querIes. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Scully
&lt;/h2&gt;

&lt;p&gt;Scully is the static site generator for Angular projects looking to embrace the &lt;a href="https://jamstack.org/"&gt;Jamstack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It will use your application and will create a static &lt;code&gt;index.html&lt;/code&gt; for each of your pages/routes. Every &lt;code&gt;index.html&lt;/code&gt; will have the content already there, and this will make your application show instantly for the user. Also, this will make your application very SEO-friendly. On top of this, your SPA will still function as it did before.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to set up an Angular app with Scully?
&lt;/h2&gt;

&lt;p&gt;In this article, we are not going very deep into the setup of Angular and Scully. For a good reference, you can have a look at the &lt;a href="https://github.com/Jefiozie/asp-example"&gt;repo&lt;/a&gt;. Below are the steps you can follow along:&lt;/p&gt;

&lt;p&gt;First, let’s set up a new Angular app&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npx ng new ng-scully --minimal&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Second, let’s add Scully to the Angular application&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ng add @scullyio/init&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Scully schematics will do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add Scully dependencies to &lt;code&gt;package.json&lt;/code&gt; and install it&lt;/li&gt;
&lt;li&gt;Import &lt;code&gt;ScullyLibModule&lt;/code&gt; to &lt;code&gt;AppModule&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;'zone.js/dist/task-tracking'&lt;/code&gt; to &lt;code&gt;polyfills.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;scully.&amp;lt;project_name&amp;gt;.config.ts&lt;/code&gt; to the root directory. This is Scully configuration file that we will utilize to configure Scully.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we have a setup that works with Angular, but we need to take one step more for this demo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ng generate @scullyio/init:blog&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The above command adds the blog modules' routes to the Angular application.&lt;br&gt;
In addition, it creates a &lt;code&gt;./blog&lt;/code&gt; folder for the blog's markdown files.&lt;/p&gt;
&lt;h1&gt;
  
  
  How to use Prisma with Scully?
&lt;/h1&gt;

&lt;p&gt;I’ve made the choice to use postgress as my database, in combination with docker.&lt;/p&gt;

&lt;p&gt;Below I’m showing you my docker-compose file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
 postgres:
    image: postgres
    ports:
      - "5432:5432"
    restart: always
    environment:
      POSTGRES_USER: prisma
      POSTGRES_PASSWORD: prisma
    volumes:
      - postgres:/var/lib/postgresql/data
volumes:
  postgres:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we only need to run it so that Prisma can connect to it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;docker-compose up -d&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can continue with Prisma, first we need to install Prisma&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npm install prisma --save-dev&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After installation we will run the init command as shown below:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npx prisma init&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This init command will setup Prisma with the required files in the directory.&lt;/p&gt;

&lt;p&gt;After this we need to change the &lt;code&gt;.env&lt;/code&gt; file with our database connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="err"&gt;DATABASE_URL="postgresql://prisma:prisma@localhost:5432/mydb?&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup Prisma configuration
&lt;/h3&gt;

&lt;p&gt;To use Prisma with Scully we first need to add the setup for Prisma.&lt;/p&gt;

&lt;p&gt;As Prisma is an ORM for a database we need to tell Prisma what tables and/ or database it is connected to. This information is placed in the &lt;code&gt;schema.prisma&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Prisma&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&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;provider&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="n"&gt;prisma&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="err"&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="err"&gt;datasource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;db&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;provider&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="n"&gt;postgresql&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;url&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="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="err"&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="err"&gt;model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Post&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;Int&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;@id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autoincrement&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;@relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&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;authorId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;references&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;id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;authorId&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="err"&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="err"&gt;model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;@id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autoincrement&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&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;String&lt;/span&gt;&lt;span class="err"&gt;?&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;String&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;@unique&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@map&lt;/span&gt;&lt;span class="p"&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="s2"&gt;"created_at"&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@updatedAt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@map&lt;/span&gt;&lt;span class="p"&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="s2"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;Post&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="k"&gt;@map&lt;/span&gt;&lt;span class="p"&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="s2"&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="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: You're occasionally using &lt;code&gt;@map&lt;/code&gt;and&lt;code&gt;@@map&lt;/code&gt;to to transform some field and model names to different column and table names in the underlying database.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This Prisma schema defines two models, each of which will map to a table in the underlying database: &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Post&lt;/code&gt;. Notice that there's also a relation (one-to-many) between the two models, via the &lt;code&gt;author&lt;/code&gt; field on &lt;code&gt;Post&lt;/code&gt; and the &lt;code&gt;posts&lt;/code&gt; field on &lt;code&gt;User&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have our schema defined, we need to &lt;code&gt;create&lt;/code&gt; our tables in our database. This can be done by running the following CLI command:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npx prisma db push&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Environment variables loaded from .env
Prisma schema loaded from prisma&lt;span class="se"&gt;\s&lt;/span&gt;chema.prisma
Datasource &lt;span class="s2"&gt;"db"&lt;/span&gt;: PostgreSQL database &lt;span class="s2"&gt;"mydb"&lt;/span&gt;, schema &lt;span class="s2"&gt;"public"&lt;/span&gt; at &lt;span class="s2"&gt;"localhost:5432"&lt;/span&gt;
The database is already &lt;span class="k"&gt;in &lt;/span&gt;&lt;span class="nb"&gt;sync &lt;/span&gt;with the Prisma schema.
✔ Generated Prisma Client &lt;span class="o"&gt;(&lt;/span&gt;3.8.1 | library&lt;span class="o"&gt;)&lt;/span&gt; to .&lt;span class="se"&gt;\n&lt;/span&gt;ode_modules&lt;span class="se"&gt;\@&lt;/span&gt;prisma&lt;span class="se"&gt;\c&lt;/span&gt;lient &lt;span class="k"&gt;in
&lt;/span&gt;75ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As our database is ready to be used, let’s add some data. We are going to use Prisma Studio, this is an easy way to explore and manipulate the data. You can open up Prisma Studio by running&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;npx prisma studio&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create a Prisma Scully Plugin
&lt;/h3&gt;

&lt;p&gt;As we now have an operating database and an ORM library (Prisma) we now can use all these parts to receive the data and use it in Scully. Let’s start by creating the first basis for our custom plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;PrismaClient&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="s2"&gt;@prisma/client&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;HandledRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;registerPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;RouteConfig&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;@scullyio/scully&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;convertAndInjectContent&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="s2"&gt;@scullyio/scully/src/lib/renderPlugins/content-render-utils/convertAndInjectContent&lt;/span&gt;&lt;span class="dl"&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;const&lt;/span&gt; &lt;span class="nx"&gt;prismaPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prismaPlugin&lt;/span&gt;&lt;span class="dl"&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;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&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;routerPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&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="kr"&gt;string&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="nx"&gt;RouteConfig&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;// here we are looking up all posts&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;post&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="c1"&gt;// where the published property is true&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;published&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;// and we include the author&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// and we only want the author's name&lt;/span&gt;
        &lt;span class="na"&gt;select&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="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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// let's loop over all posts&lt;/span&gt;
    &lt;span class="nx"&gt;data&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;post&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;// and return a new route for each post&lt;/span&gt;
      &lt;span class="kd"&gt;const&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;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&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;=&lt;/span&gt; &lt;span class="nx"&gt;post&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="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/blog/&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&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;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;author&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;content&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;as&lt;/span&gt; &lt;span class="nx"&gt;HandledRoute&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="nx"&gt;registerPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;router&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prismaPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;routerPlugin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;prismaDomPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;HandledRoute&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;route&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;dom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// here we use the power of scully and use the filehandler to convert the content to html&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;convertAndInjectContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dom&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;data&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error during contentText rendering`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nx"&gt;registerPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postProcessByDom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prismaPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prismaDomPlugin&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 break this code down from the top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;PrismaClient&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/client&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;logError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registerPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RouteConfig&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/scully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//define our plugin name&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prismaPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prismaPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// setup our PrismaClient&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// our router plugin&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routerPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&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="kr"&gt;string&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="nx"&gt;RouteConfig&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="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 we are going to retrieve the &lt;code&gt;posts&lt;/code&gt; with the Prisma client. When all the data is gathered we will return new routes that will be used on our post-render step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&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;routerPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&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="kr"&gt;string&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="nx"&gt;RouteConfig&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;// here we are looking up all posts&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;post&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="c1"&gt;// where the published property is true&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;published&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;// and we include the author&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// and we only want the author's name&lt;/span&gt;
        &lt;span class="na"&gt;select&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="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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// let's loop over all posts&lt;/span&gt;
    &lt;span class="nx"&gt;data&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;post&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;// and return a new route for each post&lt;/span&gt;
      &lt;span class="kd"&gt;const&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;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&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;=&lt;/span&gt; &lt;span class="nx"&gt;post&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="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/blog/&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&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;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;author&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;content&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;as&lt;/span&gt; &lt;span class="nx"&gt;HandledRoute&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;The post-process plugin is used to transform the render HTML. In our custom plugin, we make use of the Scully system, the &lt;code&gt;converAndInjectContent&lt;/code&gt; function will look at the &lt;code&gt;fileHandler&lt;/code&gt; plugins, and if it finds an extension of a file type.  In our case, it will look for the &lt;code&gt;fileHandler&lt;/code&gt; for markdown files. This plugin will transform our data coming from the database from markdown to HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;prismaDomPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;HandledRoute&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;route&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;dom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// here we use the power of scully and use the filehandler to convert the content to html&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;convertAndInjectContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dom&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;data&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error during contentText rendering`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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 that we have set up our plugin, we need to make one new change to our Scully configuration. We need to change the original blog route to use our custom plugin, first, we need to import our custom plugin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;prismaPlugin&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="s2"&gt;./scully/plugins/plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to define our router and post process plugin to being used in our blog route.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;routes&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;/blog/:slug&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="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prismaPlugin&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, we are ready to run our Scully system to scan for new routes, run &lt;code&gt;npx scully --scan&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="nv"&gt;$ &lt;/span&gt;npx scully &lt;span class="nt"&gt;--scan&lt;/span&gt;
  ✔ new Angular build files imported
  ✔ Starting servers &lt;span class="k"&gt;for &lt;/span&gt;project &lt;span class="s2"&gt;"asp-example"&lt;/span&gt;
  ✔ Started Angular distribution server on &lt;span class="s2"&gt;"http://localhost:1864/"&lt;/span&gt; 
  ✔ Started Scully static server on &lt;span class="s2"&gt;"http://localhost:1668/"&lt;/span&gt;
  ✔ Scully Development Server is up and running
  ✔ Puppeteer is being launched
  ✔ Successfully scanned Angular app &lt;span class="k"&gt;for &lt;/span&gt;routes
  ✔ Successfully added routes created from routePlugins
  ✔ Route list created &lt;span class="k"&gt;in &lt;/span&gt;files:
     &lt;span class="s2"&gt;".&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;rc&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s2"&gt;ssets&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;cully-routes.json"&lt;/span&gt;,
     &lt;span class="s2"&gt;"dist&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;tatic&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s2"&gt;ssets&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;cully-routes.json"&lt;/span&gt;,
     &lt;span class="s2"&gt;"dist&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s2"&gt;sp-example&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s2"&gt;ssets&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;cully-routes.json"&lt;/span&gt;

  ✔ Route &lt;span class="s2"&gt;"/blog"&lt;/span&gt; rendered into &lt;span class="s2"&gt;".&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;ist&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;tatic&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="s2"&gt;log&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;ndex.html"&lt;/span&gt; 
  ✔ Route &lt;span class="s2"&gt;"/home"&lt;/span&gt; rendered into &lt;span class="s2"&gt;".&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;ist&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;tatic&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;ome&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;ndex.html"&lt;/span&gt; 
  ✔ Route &lt;span class="s2"&gt;"/"&lt;/span&gt; rendered into &lt;span class="s2"&gt;".&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;ist&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;tatic&lt;/span&gt;&lt;span class="se"&gt;\i&lt;/span&gt;&lt;span class="s2"&gt;ndex.html"&lt;/span&gt; 
  ✔ Route &lt;span class="s2"&gt;"/blog/1"&lt;/span&gt; rendered into &lt;span class="s2"&gt;".&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;ist&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;tatic&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="s2"&gt;log&lt;/span&gt;&lt;span class="se"&gt;\1\i&lt;/span&gt;&lt;span class="s2"&gt;ndex.html"&lt;/span&gt; 

Total &lt;span class="nb"&gt;time &lt;/span&gt;used 5.74 seconds
  4 pages have been created
  Rendering the pages took 2.99 seconds
  That is 1.34 pages per second,
  or 749 milliseconds &lt;span class="k"&gt;for &lt;/span&gt;each page.

  Finding routes &lt;span class="k"&gt;in &lt;/span&gt;the angular app took 2.68 seconds
  Pulling &lt;span class="k"&gt;in &lt;/span&gt;route-data took 47 milliseconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have our first page rendered with Angular, Scully, and Prisma.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;With Prisma we have a powerful type of safety solution to connect to a database, combine this with the power of Scully we cn easily create static pages from an Angular application and upload it to a CDN.B&lt;/p&gt;

</description>
      <category>angular</category>
      <category>scully</category>
      <category>prisma</category>
    </item>
    <item>
      <title>Create a Custom Angular CLI Builder</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Mon, 22 Mar 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/jefiozie/create-a-custom-angular-cli-builder-4okb</link>
      <guid>https://dev.to/jefiozie/create-a-custom-angular-cli-builder-4okb</guid>
      <description>&lt;p&gt;In this post, we will try to explain the basics theory around the Angular CLI builders and how you can build a Custom Builder. How to build a Custom Builder will be done based on a builder we've created with a very general use case, Image Optimization. In this article, we will only show a simple example.&lt;/p&gt;

&lt;h1&gt;
  
  
  History
&lt;/h1&gt;

&lt;p&gt;Back in the day, before Angular CLI version 8 the supported method to &lt;code&gt;customize&lt;/code&gt; the Angular CLI was &lt;code&gt;ng eject&lt;/code&gt;. When using this command we would say against the Angular CLI that we as developers would handle the configuration. Maybe you didn't know but under the hood of the Angular CLI is using Webpack. So when we did run the command the Angular CLI would &lt;code&gt;eject&lt;/code&gt; the Webpack configuration to a file that we can change. Of course, this works, but you needed to know how and what to exactly change for your needs and the Webpack can be (in my opinion) a bit overwhelming when you look at it.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Angular CLI today
&lt;/h1&gt;

&lt;p&gt;As mentioned in the &lt;code&gt;History&lt;/code&gt; of the Angular CLI when writing this post we are at version 11 and a lot has been changed. The &lt;code&gt;ng eject&lt;/code&gt; command is removed from the Angular CLI. Since Angular CLI version 8 something new has made its place. This new part is called the &lt;em&gt;Builders&lt;/em&gt; API. The Builders API makes it easy to extend general parts like &lt;code&gt;ng build&lt;/code&gt;, &lt;code&gt;ng serve&lt;/code&gt;, or make a custom CLI command like &lt;code&gt;ng run mybuilder:app&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The builders we use today, every day!
&lt;/h1&gt;

&lt;p&gt;Did you ever wondered what is happening when we use &lt;code&gt;ng build&lt;/code&gt; or &lt;code&gt;ng serve&lt;/code&gt;? The Angular CLI will start a new task based on the &lt;code&gt;angular.json&lt;/code&gt; file. First, it will examine the &lt;code&gt;angular.json&lt;/code&gt; looking for the project, in a single app set up this will be always the default application.&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%2Fjefiozie.github.io%2Fassets%2Fbuilders%2Fangularjson.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%2Fjefiozie.github.io%2Fassets%2Fbuilders%2Fangularjson.png" alt="angular.json file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When this project has been found, it will look at the target, in our example, this target is &lt;code&gt;build&lt;/code&gt;. Now the Angular CLI knows enough to execute the builder! Below I've made a simple table that shows what builders are represented with a default Angular CLI command:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Builder&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;Browser builder&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;serve&lt;/td&gt;
&lt;td&gt;Dev-Server builder&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;test&lt;/td&gt;
&lt;td&gt;Karma builder&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lint&lt;/td&gt;
&lt;td&gt;TSLint builder&lt;/td&gt;
&lt;td&gt;❗ this builder is deprecated from Angular v12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;e2e&lt;/td&gt;
&lt;td&gt;Protractor&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  So what is a builder then
&lt;/h1&gt;

&lt;p&gt;Before I'm going to provide you with my explanation of builders, let's have a look at the docs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Several Angular CLI commands run a complex process on your code, such as linting, building, or testing.
The commands use an internal tool called Architect to run CLI builders, which apply another tool to accomplish the desired task. 
With Angular version 8, the CLI Builder API is stable and available to developers who want to customize the Angular CLI by adding or modifying commands. 
For example, you could supply a builder to perform an entirely new task or to change which third-party tool is used by an existing command.

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

&lt;/div&gt;



&lt;p&gt;After reading this I made the following conclusion:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;As a Angular Builder is just a function, you could refer it is a task. The Angular CLI is having an task-based system. This way we can say that the Angular CLI with the task-based system (called Architect) is delegating the tasks (builders).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For me, this made it easy in my mind to link specific commands to tasks and know that they are executed with specific commands provided by the Angular CLI.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to create a custom builder?
&lt;/h1&gt;

&lt;p&gt;Now that we've come to the part where we are going to look at the custom builder we first need to set up the project structure. Below a couple of minimal steps we need to take before we can continue with our builder:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Npm init&lt;/li&gt;
&lt;li&gt;Git init&lt;/li&gt;
&lt;li&gt;Add dependencies (minimal needed):

&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;@angular-devkit/architect&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add some npm scripts for building and testing our package&lt;/li&gt;
&lt;li&gt;Add a “builder.json” file&lt;/li&gt;
&lt;li&gt;Add "builders": "builders.json" to your package.json&lt;/li&gt;
&lt;li&gt;Add an “index.ts” file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our project structure is complete (&lt;a href="https://github.com/Jefiozie/imagization" rel="noopener noreferrer"&gt;see a full example&lt;/a&gt;)! Now we are going to create the &lt;code&gt;Builder&lt;/code&gt;, in our next article we will address how you can use/connect your custom builder with the Angular CLI. The Angular CLI team has provided an easy way to set up our custom builder so that it can be executed with the Angular CLI. We need to import the &lt;code&gt;createBuilder&lt;/code&gt; function. The &lt;code&gt;createBuilder&lt;/code&gt; function is hooking up the task-based system from within the Angular CLI and lets you use your custom builder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createBuilder, BuilderContext, BuilderOutput } from '@angular-devkit/architect'

// Schema Options
interface Options extends JsonObject {...}

// Our func. that is executed by the tasked based system
function customBuilderFunc(
  options: Options,
  context: BuilderContext,
  ): Promise&amp;lt;BuilderOutput&amp;gt; {
    // logging the message
    context.reportStatus(`Executing Custom Builder`);
    return new Promise(resolve =&amp;gt; {
        // log message when we are done
      context.reportStatus(`Done running Custom Builder 🎉`);
    });
}

export default createBuilder(customBuilderFunc);

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Builders are a very useful part of the Angular CLI that will make sure the CLI is very extensible. We can use builders for a variety of tasks that are related to the workspace(s). As builders are just functions we can do "ANYTHING". I've created a "demo" builder that is optimizing images in your workspace for better web performance. This builder will be in our next article where we will go a little bit deeper into, creating custom builders and how to hook them into your workspace. You can already have a look &lt;a href="https://github.com/Jefiozie/imagization" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you've enjoyed this article, all of this are from my personal experience. If you have any questions around builder you can always DM me on &lt;a href="https://twitter.com/jefiozie" rel="noopener noreferrer"&gt;@jefiozie&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;p&gt;Below I've added some helpful resources that you can have a look at if you're interested in builders.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://angular.io/guide/cli-builder" rel="noopener noreferrer"&gt;Angular.io - CLI Builders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://angular-builders.dev/" rel="noopener noreferrer"&gt;Angular Builder by Santosh Yadav&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/HyvZ26ofTvY" rel="noopener noreferrer"&gt;Deep Dive Into CLI Builders by Mike Hartington&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://github.com/xlayers/version-stamp" rel="noopener noreferrer"&gt;Version stamp by the xLayers team&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Jefiozie/ngx-aws-deploy" rel="noopener noreferrer"&gt;ngx-aws-deploy by Jeffrey Bosch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bampakoa/ngx-electronify" rel="noopener noreferrer"&gt;ngx-electronify by Aristeidis Bampakos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Deploy your Angular app with Scully and Cloudflare Pages</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Mon, 15 Mar 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/jefiozie/deploy-your-angular-app-with-scully-and-cloudflare-pages-3e5a</link>
      <guid>https://dev.to/jefiozie/deploy-your-angular-app-with-scully-and-cloudflare-pages-3e5a</guid>
      <description>

&lt;p&gt;In this article, we will introduce you to Scully and explain how to deploy your Angular app with Scully to Cloudflare pages. This article is for people who are interested in setting up a JAMstack app with Angular, Scully, and Cloudflare pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 ❗ Pre-requisites ❗

- You should have some basic knowledge of Angular
- You should have a Cloudflare account
- You should have a Github account

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  What is Cloudflare pages
&lt;/h1&gt;

&lt;p&gt;Cloudflare Pages is the solution for people that use JAMstack frameworks. Cloudflare Pages easily integrates with Github in a way that when you push a new version, Cloudflare automatically starts building and deploying your app on its network.&lt;/p&gt;

&lt;p&gt;If you’re not familiar with JAMstack, it’s a popular way of developing and deploying websites at scale. You can look at this resource for more information about the JAMstack &lt;a href="https://Jamstack.org/" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Coming back to Cloudflare Pages, as we said earlier it integrates with GitHub repositories if you’re hosting your source code on that platform. Once your site is configured, you can preview each commit from Cloudflare’s interface — each commit gets a unique URL and there’s a preview environment.&lt;/p&gt;

&lt;p&gt;You can collaborate with other Cloudflare users by inviting them to your Pages project. When your site looks good in the preview branch, you can push it to the production branch.&lt;/p&gt;

&lt;p&gt;So in summary, Cloudflare Pages lets you easily deploy your app without any hustle.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup your Angular app with Scully
&lt;/h1&gt;

&lt;p&gt;Before we set up our Angular app, let's have a quick look at Scully.&lt;/p&gt;

&lt;p&gt;Scully is the best static site generator for Angular projects looking to embrace the JAMstack. It will use your application and will create a static &lt;code&gt;index.html&lt;/code&gt; for each of your pages/routes. Every &lt;code&gt;index.html&lt;/code&gt; will have the content already there, and this will make your application show instantly for the user. Also, this will make your application very SEO-friendly. On top of this, your SPA will still function as it did before. A big advance of Scully is that it has an easy-to-use and extendible plugins system that will allow you to manipulate routes and content.&lt;/p&gt;

&lt;p&gt;For this article, we will set up a really simple app, just so we can learn about Cloudflare Pages.&lt;/p&gt;

&lt;p&gt;Before we are get started, go to Github and create a new repo, you can also go to &lt;code&gt;https://repo.new&lt;/code&gt; this is an easy way to create a new repository at Github.&lt;/p&gt;

&lt;p&gt;Clone the repo to your local machine, now let's continue by setting up a new Angular project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @angular/cli
ng new &amp;lt;YOUR-PROJECT-NAME&amp;gt; --routing

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

&lt;/div&gt;



&lt;p&gt;By running the command above we will be provided with a simple Angular app with a router module. When the Angular CLI is finished, we will have a fresh Angular workspace, with a pre-filled demo Angular app.&lt;/p&gt;

&lt;p&gt;Let's continue by adding Scully, the Scully team has made this easy for us, we only need to run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng add @scullyio/init

--- output should be something like below ---

Installing packages for tooling via npm.
Installed packages for tooling via npm.
    Install ng-lib
    ✅️ Added dependency
UPDATE src/app/app.module.ts (466 bytes)
UPDATE src/polyfills.ts (3019 bytes)
UPDATE package.json (1310 bytes)
- Installing packages (npm)...
√ Packages installed successfully.
    ✅️ Update package.json
    ✅️ Created scully configuration file in scully.demo-cloudflare-pages.confts
CREATE scully.demo-cloudflare-pages.config.ts (196 bytes)
UPDATE package.json (1384 bytes)
CREATE scully/tsconfig.json (450 bytes)
CREATE scully/plugins/plugin.ts (305 bytes)

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

&lt;/div&gt;



&lt;p&gt;We are now ready to use Scully with Angular, to do this we first need to build the Angular project. This can be done by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng build --prod

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

&lt;/div&gt;



&lt;p&gt;Now that the Angular project is built, Scully can do its work. Run Scully with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx scully

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

&lt;/div&gt;



&lt;p&gt;We did it! We turned your Angular app into a pre-rendered static site, we can now push our changes to our Github repo.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup your repository for Cloudflare pages
&lt;/h1&gt;

&lt;p&gt;We are now ready to connect or freshly created app with Cloudflare pages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your Cloudflare account&lt;/li&gt;
&lt;li&gt;On the right, click on Pages&lt;/li&gt;
&lt;/ol&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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fcloudflare_menu.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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fcloudflare_menu.png" alt="Preview of Cloudflare menu"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3.Click on "Create a project"&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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fcloudflare_create_project.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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fcloudflare_create_project.png" alt="Preview of Cloudflare Create a project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4.Connect your Github account&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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fconnect_github.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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fconnect_github.png" alt="Preview of Connect Github"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5.When you successfully connected to Github, you can choose a repository. In our example, I'm selecting &lt;code&gt;demo-cloudflare-pages&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;6.After selecting the project, click on &lt;strong&gt;Begin Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;7.Scroll to &lt;em&gt;Build Settings&lt;/em&gt; and fill in the same setting as the picture below&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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fbuild_settings.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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fbuild_settings.png" alt="Preview of Build settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8.CLick on &lt;em&gt;Deploy&lt;/em&gt;&lt;br&gt;
9.Cloudflare will start working on building your app, this will fail, this is intended, don't worry we will fix it in the next chapter.&lt;/p&gt;
&lt;h1&gt;
  
  
  Deploy it with Cloudflare pages
&lt;/h1&gt;

&lt;p&gt;We are almost ready to deploy our app, in the previous part we configured everything to be ready for deployment. But we still need to tweak a couple of things before we can use Cloudflare pages. So let's make these changes!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your package.json file and add the following snippet to the &lt;code&gt;scripts&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "buildStaticApp": "ng build --prod &amp;amp;&amp;amp; npx scully --scanRoutes"

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

&lt;/div&gt;


&lt;p&gt;2.Open up your scully configuration file, in our case &lt;code&gt;scully.demo-cloudflare-pages.config.ts&lt;/code&gt;&lt;br&gt;
3.Add the following snippet to the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;puppeteerLaunchOptions: {
    args: [
      '--disable-gpu',
      '--renderer',
      '--no-sandbox',
      '--no-service-autorun',
      '--no-experiments',
      '--no-default-browser-check',
      '--disable-dev-shm-usage',
      '--disable-setuid-sandbox',
      '--no-first-run',
      '--no-zygote',
      '--single-process',
      '--disable-extensions',
    ],
  }

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

&lt;/div&gt;



&lt;p&gt;These changes are needed as Cloudflare runs in a VM or Docker, and we cannot have a "real" chromium spin up. This way we make sure that the bare minimum is enabled for puppeteer so that it can run in the VM or Docker.&lt;/p&gt;

&lt;p&gt;4.Now commit and push the changes to the Github repo&lt;/p&gt;

&lt;p&gt;5.Cloudflare will automatically pick up the changes, let's go back to our Cloudflare project and we will see it is building our app:&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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fcloudflare_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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fcloudflare_building.png" alt="Preview of Build settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6.If everything goes well, we have successfully deployed our app!! &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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fcloudflare_deploy.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%2Fjefiozie.github.io%2Fassets%2Fcloudflare%2Fcloudflare_deploy.png" alt="Preview of Build settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In this article, we have made our Angular app a JAMstack app with the help of Scully and deployed it with Cloudflare pages! Below are some resources that I found helpful when using Cloudflare pages and Scully. You can find the example repo &lt;a href="https://github.com/Jefiozie/demo-cloudflare-pages" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions send me DM via my twitter profile &lt;a href="https://twitter.com/jefiozie" rel="noopener noreferrer"&gt;@jefiozie&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Jefiozie/demo-cloudflare-pages" rel="noopener noreferrer"&gt;Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://scully.io/" rel="noopener noreferrer"&gt;Scully&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pages.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare Pages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>showdev</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Automate your community website easily with automation and Scully</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Sun, 29 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/jefiozie/automate-your-community-website-easily-with-automation-and-scully-2l2g</link>
      <guid>https://dev.to/jefiozie/automate-your-community-website-easily-with-automation-and-scully-2l2g</guid>
      <description>

&lt;p&gt;Last year I've joined the Dutch Angular Group as an Organizer and I can say I love it! 💖 Before I'm starting with my blog post I would like to give a shoutout to my fellow co-organizers and especially the founder of the Dutch Angular Group &lt;a href="https://twitter.com/esosanderelias"&gt;Sander Elias&lt;/a&gt; and &lt;a href="https://twitter.com/bonnster75"&gt;Bonnie Brennan&lt;/a&gt; for the introductions and faith in me.&lt;/p&gt;

&lt;p&gt;Thank you for letting me be a part of the DAG Community and have the privilege to help, show and organize great events where people can learn 🙏🏻&lt;/p&gt;

&lt;h1&gt;
  
  
  How it all started
&lt;/h1&gt;

&lt;p&gt;As a community group, we have had a custom domain name for several years, but in the last couple of years, we have never used it. Since the first beta of Scully, I've built a couple of POC and my blog with it. Based on my experience there I thought why not build something where our community can go, look at the schedule, have some general information about our group, and see our Code of Conduct.&lt;/p&gt;

&lt;p&gt;This is where my journey with a community website started, but this is not the place where I thought about automation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Let's automate things
&lt;/h1&gt;

&lt;p&gt;When I started with the organization of the DAG events, I didn't know anything about how to organize something, where to start, did we need a monthly schedule? All these things were new for me so with our organizer's team we just started by planning &lt;em&gt;one&lt;/em&gt; event. But, then came COVID-19, so we needed to adjust to the regulation, we quickly adopted a streaming service what we could use to do our first online session. It was a great adventure and I just made some quick Artwork for the announcements on our Meetup group. I have to say, I was pretty excited, my first organized event with two GDE's! 😎 It went great!&lt;/p&gt;

&lt;p&gt;After organizing 5 events, It occurred to me that I needed to do a repetitive task like every week for an event. Create an event, make artwork, twitter announcements, extra Twitter messages, etc.&lt;/p&gt;

&lt;p&gt;Last week I had some spare time and I thought let's see If I could automate some stuff so that I have less work to do for the organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did we do this
&lt;/h2&gt;

&lt;p&gt;Scully is a static site generator that will convert a normal Angular app to a static website. When I had the idea to build this, one of the requirements was to have our Meetup calendar placed on our website. As Scully has a really easy and great way to create custom plugins, we've created a &lt;a href="https://www.npmjs.com/package/scully-plugin-meetup"&gt;Meetup plugin&lt;/a&gt; that will look at your Meetup group and will get all the 100 latest events. With these 100 events, it will generate all these events in a specific way that we can use statically in our website.&lt;/p&gt;

&lt;p&gt;With this in place, the only missing part was to automate the deployment process. As Github has a great set of Actions to assist you we've used one of these to deploy our static content, however, Github Actions cannot do anything without being notified. Looking at our automation part we thought could we trigger a Github action by doing an HTTP request? And we found out that this is supported by Github Actions, you just need to add the following snippet to your Github workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on:
  repository_dispatch:
    types:
      - A EVENT NAME

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

&lt;/div&gt;



&lt;p&gt;By using Zapier we could now do a POST request to Github Actions to trigger our generation and deployment of our community website without doing any manual changes! If you would like to know about this specific part of Github Action, &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#repository_dispatch"&gt;click here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dQx3ekSb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://jefiozie.github.io/articles/article/assets/zapier_deployment.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dQx3ekSb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://jefiozie.github.io/articles/article/assets/zapier_deployment.png" alt="Preview of Meetup deployment automation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Thank you and start using the plugin
&lt;/h1&gt;

&lt;p&gt;Thank you for reading this post, feel free to DM me on &lt;a href="https://twitter.com/jefiozie"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've made the plugin available for everybody to use, you can find it &lt;a href="https://www.npmjs.com/package/scully-plugin-meetup"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Dutch-Angular-Group/website/"&gt;Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Dutch-Angular-Group/website/tree/main/libs/scully-plugin-meetup"&gt;Repository of the plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dutchangular.org"&gt;Community Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zapier.com"&gt;Zapier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://scully.io/"&gt;Scully&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>showdev</category>
      <category>automation</category>
    </item>
    <item>
      <title>Easy version stamp your Angular app🦶</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Fri, 23 Oct 2020 15:53:17 +0000</pubDate>
      <link>https://dev.to/xlayers/easy-version-stamp-your-angular-app-39jn</link>
      <guid>https://dev.to/xlayers/easy-version-stamp-your-angular-app-39jn</guid>
      <description>&lt;p&gt;Did you ever create bash scripts that you've used in multiple projects to do version stamp your Angular application?&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%2Fgithub.com%2Fxlayers%2Fversion-stamp%2Fraw%2Fmain%2Fassets%2Fxlayers_stamp.png%3Fraw%3Dtrue" 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%2Fgithub.com%2Fxlayers%2Fversion-stamp%2Fraw%2Fmain%2Fassets%2Fxlayers_stamp.png%3Fraw%3Dtrue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today we are very happy to announce 🎉 &lt;strong&gt;&lt;strong&gt;@xlayers/version-stamp&lt;/strong&gt;&lt;/strong&gt; 🎉 an Angular Builder that will help you to version stamp your Angular apps.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to use version-stamp
&lt;/h1&gt;

&lt;p&gt;1.Navigate to your Angular project&lt;/p&gt;

&lt;p&gt;2.Add @xlayers/version-stamp to your 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 add @xlayers/version-stamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package will make some small changes to the angular.json&lt;/p&gt;

&lt;p&gt;3.Add the following snippet (only the version property) to your environment 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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;production&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;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_BUILD_HASH_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.Use the version that the packages has provided in your environment properties.&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;environment&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="s2"&gt;../environment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;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="s2"&gt;xlayers-root&lt;/span&gt;&lt;span class="dl"&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;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&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;5.Now you can stamp your app by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng run &amp;lt;your-app&amp;gt;:stamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or use &lt;code&gt;--version&lt;/code&gt; for a specific version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng run &amp;lt;your-app&amp;gt;:stamp &lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.2.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6.When we navigate to our &lt;code&gt;dist&lt;/code&gt; folder we can look at our code and see something that is similiar to the picture below&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyht3b1b9rlju8y9umkvu.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyht3b1b9rlju8y9umkvu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Thank you
&lt;/h1&gt;

&lt;p&gt;Thank you for reading our article, we hope this article encourages you to try xLayers products as it is fairly easy to use and provides a lot of value in the collaboration between designers and developers.&lt;/p&gt;

&lt;p&gt;Would you like to help us with xLayers by contributing? We have issues for first-timers and we are willing to help you in all possible ways. Next to contributing you can help us by donating, this way we can develop more features that are on our roadmap.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>showdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A new type of Angular budget on the block</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Tue, 10 Mar 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/jefiozie/a-new-type-of-angular-budget-on-the-block-4l8</link>
      <guid>https://dev.to/jefiozie/a-new-type-of-angular-budget-on-the-block-4l8</guid>
      <description>&lt;p&gt;Before we are going to look at the new budget that recently has been added to the Angular CLI, let’s do a small recap on what budgets are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Angular Budgets?
&lt;/h2&gt;

&lt;p&gt;The official &lt;a href="https://angular.io/guide/build#configure-size-budgets"&gt;documentation&lt;/a&gt; says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As applications grow in functionality, they also grow in size. The CLI allows you to set size thresholds in your configuration to ensure that parts of your application stay within size boundaries that you define.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, bundles are the set of compiled JavaScript files, which are produced by the build process. Angular Budgets allow us to set a threshold around the size of these bundles. With the help of Angular Budgets, we can easily define a condition where we would expect a warning or error threshold if the size of a bundle increases. When an error is provided our build will fail with a bundle error.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new type of budget on the block:
&lt;/h2&gt;

&lt;p&gt;With the release of Angular CLI version 9.x, a new budget type was introduced. This type is being called the &lt;a href="https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/build_angular/src/angular-cli-files/plugins/any-component-style-budget-checker.ts"&gt;&lt;code&gt;anyComponentStyle&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How will this budget help me?
&lt;/h2&gt;

&lt;p&gt;Good question, when we develop an application (or library) our bundle sizes will increase. This is how the development lifecycle is and always will be. However, we like to have them as small as possible and that is where budgets come into play. Where the regular budgets will look at our JavaScript bundles, the &lt;code&gt;anyComponentStyle&lt;/code&gt; will look at our &lt;strong&gt;individual&lt;/strong&gt; component CSS files.&lt;/p&gt;

&lt;p&gt;Some interesting things to know about the rules that apply with this budget:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The budget will throw a warning or an error if any component has styles bigger than the configured threshold, but it will &lt;strong&gt;NOT&lt;/strong&gt; raise any warning or error if the global style is &lt;em&gt;HUGE&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The budget will &lt;strong&gt;only&lt;/strong&gt; check individual components&lt;/li&gt;
&lt;li&gt;At this moment it only supports CSS but there is a &lt;a href="https://github.com/angular/angular-cli/pull/17096"&gt;PR&lt;/a&gt; incoming to support other extensions as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How is the new budget type defined
&lt;/h2&gt;

&lt;p&gt;Previously, the &lt;code&gt;angular.json&lt;/code&gt; contained a default budget rule like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"budgets": [
 {
   "type": "initial",
   "maximumWarning": "2mb",
   "maximumError": "5mb"
 }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When migrating to the new version of the Angular CLI, you will find a new budget added to the &lt;code&gt;angular.json&lt;/code&gt;, which looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"budgets": [
 {
   "type": "initial",
   "maximumWarning": "2mb",
   "maximumError": "5mb"
 },
 {
   "type": "anyComponentStyle",
   "maximumWarning": "6kb",
   "maximumError": "10kb"
 }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This new definition is where you can set the constraints for a warning or error message when a component’s CSS file is exceeding this size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank you!
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed this article! I would appreciate if you would share this article to spread the word around this new cool budget!Also big thanks for some great reviewers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/frederikprijck"&gt;Frederik Prijck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/SantoshYadavDev"&gt;Santosh Yadav&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/samvloeberghs"&gt;Sam Vloeberghs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please, don’t hesitate to ping me if you have any questions around Angular via Twitter &lt;a href="https://twitter.com/jefiozie"&gt;@jefiozie&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;RESOURCES&lt;/em&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://angular.io/guide/cli-builder"&gt;Angular CLI Builders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://angular.io/guide/build#configuring-size-budgets"&gt;Angular Budgets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/angular/angular-cli/pull/17096"&gt;PR for supporting Sass&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>angularcli</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Introducing NGX-AWS-DEPLOY: easily deploy your Angular app to AWS S3 from the Angular CLI</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Mon, 13 Jan 2020 12:19:37 +0000</pubDate>
      <link>https://dev.to/jefiozie/introducing-ngx-aws-deploy-easily-deploy-your-angular-app-to-aws-s3-from-the-angular-cli-bpa</link>
      <guid>https://dev.to/jefiozie/introducing-ngx-aws-deploy-easily-deploy-your-angular-app-to-aws-s3-from-the-angular-cli-bpa</guid>
      <description>&lt;p&gt;We are very happy and excited to share with you a new Angular Builder: &lt;a href="https://www.npmjs.com/package/@jefiozie/ngx-aws-deploy"&gt;NGX-AWS-DEPLOY&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What is an Angular Builder?
&lt;/h1&gt;

&lt;p&gt;Most of you probably know that the Angular CLI has some CLI commands, sometimes these commands run a complex process, such as linting, building or testing. Internally they make use of the &lt;em&gt;Architect&lt;/em&gt; tool to run CLI Builders. With Angular version 8, the CLI Builder API is stable and available to developers who want to customize the Angular CLI by adding or modifying commands.&lt;/p&gt;

&lt;p&gt;An Angular Builder is a function that will be executed by using a specific Angular CLI Command. Because you can run &lt;strong&gt;ANY&lt;/strong&gt; command and can make &lt;strong&gt;ANY&lt;/strong&gt; implementation you almost could say: &lt;em&gt;The sky is the limit by adopting Angular Builders&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What is an Angular Deployment Builder?
&lt;/h1&gt;

&lt;p&gt;In Angular CLI version 8.3 the Angular team added a new CLI command: &lt;code&gt;ng deploy&lt;/code&gt;The existing builder APIs are still used, but the usage has been simplified. It has a very short syntax, because by default it will deploy the default project in the workspace. And the most important point: It is an invitation to the community to standardize deployments under the umbrella of the Angular CLI!&lt;/p&gt;

&lt;h1&gt;
  
  
  Why I created this Angular Builder
&lt;/h1&gt;

&lt;p&gt;I started the project because I was interested in the Angular Builders API. As I’m loving the way you could do a deployment of an Angular App by using &lt;a href="http://github.com/Azure/ng-deploy-azure"&gt;@azure/ng-deploy&lt;/a&gt;, I had the idea to do this for Amazon’s S3 storage. In the past, I’ve done some work with Amazon and needed to do several steps to upload an Angular app to AWS S3. After looking at the AWS SDK and some examples on the documentation, I created the builder that we now call: &lt;strong&gt;&lt;em&gt;NGX-AWS-DEPLOY&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  How can we use it?
&lt;/h1&gt;

&lt;p&gt;A builder must be configured for an existing Angular project. But don’t worry! The installation is very simple, because you only have to execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng add @jefiozie/ngx-aws-deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the command it will install the package for you and ask you a couple of questions to setup the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/cf7a2cece4ed526301ac02f0d62f1fab/e199c/questions.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ycFPwQAS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://jefiozie.github.io/static/cf7a2cece4ed526301ac02f0d62f1fab/b9e4f/questions.png" alt="Questions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this, you are ready to deploy your app to AWS S3, just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Roadmap?
&lt;/h1&gt;

&lt;p&gt;What does the future bring for this package? I’m not sure yet, I would love to see some feature issues created by the community! Maybe there are use cases I’ve not yet thought of so please submit your issue or feature on the &lt;a href="https://github.com/Jefiozie/ngx-aws-deploy"&gt;NGX-AWS-DEPLOY Repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the ideas are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Credentials via environment variables &lt;a href="https://github.com/Jefiozie/ngx-aws-deploy/issues/12"&gt;#12&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Multi-region upload&lt;/li&gt;
&lt;li&gt;Better progress indicator&lt;/li&gt;
&lt;li&gt;Allow compressing the app and upload it (maybe an edge case)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Thank you
&lt;/h1&gt;

&lt;p&gt;Thank you for reading this post. I hope you will enjoy &lt;strong&gt;&lt;em&gt;NGX-AWS-DEPLOY&lt;/em&gt;&lt;/strong&gt;. A big thanks to the Angular CLI team (and everybody who helped) with creating the builders API. I love it! 👍🏻&lt;/p&gt;

&lt;p&gt;Thanks to people who reviewed this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/bjeaurn"&gt;@bjeaurn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/JohannesHoppe"&gt;@JohannesHoppe&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;RESOURCES&lt;/em&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://angular.io/guide/cli-builder"&gt;Angular CLI Builders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mgechev/cli-builders-demo"&gt;Example of CLI Builder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/angular-schule/ngx-deploy-starter"&gt;ngx-deploy-starter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://angular-builders.dev"&gt;Angular Builders - website with builders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/angular-in-depth/angular-cli-builder-26f0981fb7f3"&gt;InDepth - Angular Builders&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>aws</category>
      <category>showdev</category>
      <category>web</category>
    </item>
    <item>
      <title>From design to code</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Sat, 03 Aug 2019 12:16:21 +0000</pubDate>
      <link>https://dev.to/xlayers/from-design-to-code-5f8a</link>
      <guid>https://dev.to/xlayers/from-design-to-code-5f8a</guid>
      <description>&lt;p&gt;It has been a while since we have updated you on xLayers. Within this article, we will introduce xLayers and the value it can add to your daily work as a designer or as a developer. But first, let's repeat the goal of xLayers, this will help us understand why xLayers is created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Goal&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;xLayers is an online Web application which aims to bridge the gap between designers and developers. Its mission is to allow an easy collaborate world between Designers and Developer so that they can iterate fast.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we all have an understanding of the main goal, let’s move on and see how xLayers is working. Our journey within xLayers will consist of three parts where we go from a sketch file to code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1, From SketchApp to xLayers:
&lt;/h2&gt;

&lt;p&gt;Before we can start using xLayers, we need a ‘sketch’ file. With this file, we can start using xLayers! (if you don’t have any sketch files no worries we provide a couple of demo file)&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://xlayers.dev"&gt;https://xlayers.dev&lt;/a&gt;, we will arrive on the landing page, on this page you will see a variety of information on all the things xLayers will provide you. Have a look at it, for now, we will continue our journey on how we can create code from Sketch.&lt;/p&gt;

&lt;p&gt;On the landing page, you see a button with “Get Started”, let’s press this button.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2M8n5owV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/l4m7dsp8oxrg74vjwiex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2M8n5owV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/l4m7dsp8oxrg74vjwiex.png" alt="xlayers-landing-page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can upload our sketch file or select one of the demo files. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BX1wIQth--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1vnegtzuppklyeqdr2ax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BX1wIQth--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1vnegtzuppklyeqdr2ax.png" alt="upload your file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;xLayers, will analyze this file and create a live example within your browser. This live example is called the component viewer, it represents the designed sketch file in HTML. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--haoqYfBZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6jdhgyavz41pez80ykcp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--haoqYfBZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6jdhgyavz41pez80ykcp.png" alt="xlayers-component-viewer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, let’s continue to the next part!&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2, Collaboration
&lt;/h2&gt;

&lt;p&gt;Now that we are in the component viewer, we encourage the designer and developers to sit side by side and have a conversation about the designed parts that are visible on the component viewer. &lt;/p&gt;

&lt;p&gt;The goal and the power of xLayers is now visible, you can use the viewer to have a conversation about the different aspects of the component, see if all parts are correct. Within the viewer, you can easily see each created page with their layers by using the left sidebar or click on the layers. By selecting a layer, the layer will be highlighted by a red indication border.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dKrmJ59y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jf42g5611c5mtxolp6uz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dKrmJ59y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jf42g5611c5mtxolp6uz.png" alt="xlayer-layer-selection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To see more details you can use the toolbar on the top, these actions all have their own goal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--28II81Yy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/06m3b545fq9dj8k79fmz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--28II81Yy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/06m3b545fq9dj8k79fmz.png" alt="xlayers-action-bar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Zooming In:
&lt;/h3&gt;

&lt;p&gt;The zoom-in action can be used by clicking on the magnifying glass (as shown below) or by holding Ctrl + moving your mouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hoeI0Rud--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/g0ruresr887gl01d7ul0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hoeI0Rud--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/g0ruresr887gl01d7ul0.png" alt="xlayers-zoom-in"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Zooming Out:
&lt;/h3&gt;

&lt;p&gt;The zoom-out action can be used by clicking on the magnifying glass (as shown below) or by holding Ctrl + moving your mouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DTV0mNCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bpe3m33lmfwdoaoh2rwb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DTV0mNCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bpe3m33lmfwdoaoh2rwb.png" alt="xlayers-zoom-out"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reset to initial zoom:
&lt;/h3&gt;

&lt;p&gt;By pressing the “basic magnifying glass” the zoom level will be reset to the initial zoom.&lt;/p&gt;

&lt;h3&gt;
  
  
  3D:
&lt;/h3&gt;

&lt;p&gt;When pressing the 3D action, you have the ability to look through all the different layers in a 3D viewing way. By pressing the left shift and moving your mouse to a side you can even navigate around all of the different layers and see it from different angles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3ZQqMrVi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/t38uwvth40cptja7nrb6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3ZQqMrVi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/t38uwvth40cptja7nrb6.png" alt="xlayers-action-3d"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3, Source code:
&lt;/h2&gt;

&lt;p&gt;The last action in the toolbar is the action which navigates you to the code generation. After navigation, you will have a set of generated code based on the sketch file. By default it will open with the selection of the Angular framework, however, you have the possibility to select a framework to your own choice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sRYd_qJV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/v647jqw3bpkakgouo3v0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sRYd_qJV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/v647jqw3bpkakgouo3v0.png" alt="xlayers-code"&gt;&lt;/a&gt;&lt;br&gt;
On this page, you also have the ability to use the toolbar. Only this time you have fewer options. One of these options is to go and use StackBlitz as your online IDE. When this action is enabled, all code will be published to Stackblitz and you will have a setup of the project that you can use in an online IDE 🚀.&lt;/p&gt;

&lt;p&gt;The second option is to download all of your code. With this option, you will be provided with a compressed zip that can be downloaded to your machine. This way you can integrate it easily within the desired repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What brings the future of xLayers?
&lt;/h2&gt;

&lt;p&gt;At the moment we are focussing on the following topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support more than only web frameworks, Xamarin support is already there&lt;/li&gt;
&lt;li&gt;Have a completely new sketch parser to handle bitmaps and SVG and many more extensibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thank you
&lt;/h2&gt;

&lt;p&gt;Thank you for reading our article, we hope this article encourages you to try xLayers as it is fairly easy to use and provides a lot of value in the collaboration between designers and developers.&lt;/p&gt;

&lt;p&gt;Would you like to help us with xLayers by contributing? We have issues for first-timers and we are willing to help you in all possible ways. Next to contributing you can help us by donating, this way we can develop more features that are on our roadmap.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>xLayers - v1.0.0-beta 5</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Wed, 19 Jun 2019 20:50:41 +0000</pubDate>
      <link>https://dev.to/xlayers/xlayers-v1-0-0-beta-5-47ld</link>
      <guid>https://dev.to/xlayers/xlayers-v1-0-0-beta-5-47ld</guid>
      <description>&lt;p&gt;We are happy to announce a new version of xLayers: v1.0.0-beta 5 🎉🎉&lt;/p&gt;

&lt;p&gt;Within this release, we have some new features, improvements and small fixes.&lt;/p&gt;

&lt;p&gt;Some highlights of this release are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upgraded to Angular V8&lt;/li&gt;
&lt;li&gt;Support for Xamarin &lt;/li&gt;
&lt;li&gt;Multi translated (ENG, NL)&lt;/li&gt;
&lt;li&gt;Updated support for Stencil One&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the complete changelog &lt;a href="https://github.com/xlayers/xlayers/releases/tag/1.0.0-beta.5" rel="noopener noreferrer"&gt;click here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a sample Xamarin code generated from the Sketch design:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgbu46cfc6yls2gkt3m3b.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgbu46cfc6yls2gkt3m3b.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any questions or want to provide us feedback send us a DM or create an issue at our Github.&lt;/p&gt;

&lt;p&gt;Follow &lt;code&gt;@xlayers_&lt;/code&gt; on Twitter for more updates about xLayers. You also can support us by make a donation on &lt;a href="https://opencollective.com/xlayers" rel="noopener noreferrer"&gt;Open Collective&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>design</category>
    </item>
    <item>
      <title>Building a drawer with Stencil</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Wed, 05 Jun 2019 17:33:41 +0000</pubDate>
      <link>https://dev.to/jefiozie/building-a-drawer-with-stencil-2p06</link>
      <guid>https://dev.to/jefiozie/building-a-drawer-with-stencil-2p06</guid>
      <description>&lt;p&gt;For the past couple of weeks, I’ve been working on a “Design System” that is using Stencil. Today I want to share some basics on how I build a drawer with Stencil, with you, the community 🙏🏻.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a drawer
&lt;/h2&gt;

&lt;p&gt;A drawer is a component, designed to add collapsible side content (often navigation) alongside some content. Further, in this article, you will see the end result of our drawer (&lt;em&gt;that won’t be a perfectly styled example&lt;/em&gt;😆).&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture of the drawer
&lt;/h2&gt;

&lt;p&gt;Our drawer has three main parts:&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/9737f229489869d7ed52de2683ff9096/93d99/drawer_architecture.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zhpj7mOb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://jefiozie.github.io/static/9737f229489869d7ed52de2683ff9096/b9e4f/drawer_architecture.png" alt="Preview of drawer architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A container&lt;/li&gt;
&lt;li&gt;A slot for the drawer content&lt;/li&gt;
&lt;li&gt;A slot for the content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The container will serve as a wrapper. It will help us with the styling of our internal parts. The content slot is our placeholder for the content that will be served in the slot. Often this will be navigation. Finally, we have a slot for application content. Here you will provide the main content of your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;As the focus of this article is on creating a drawer container I will provide a brief introduction on setting up a new project.&lt;/p&gt;

&lt;p&gt;First, we are going to create a new Stencil project, with some simple steps provided by the &lt;a href="https://stenciljs.com/docs/getting-started"&gt;Getting Starred of Stencil&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a new project:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You will be prompt to choose a project type, choose &lt;code&gt;component&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;? Pick a starter › - Use arrow-keys. Return to submit.

❯ ionic-pwa Everything you need to build fast, production-ready PWAs
   app Minimal starter for building a Stencil app or website
   component Collection of web components that can be used anywhere
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you need to provide a project name, let’s fill in &lt;code&gt;example-drawer&lt;/code&gt;. And that is it now we are ready to start working on our component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;After opening the project in your favorite editor open the &lt;code&gt;my-component.tsx&lt;/code&gt; file and remove all of the default code and replace it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, Host, h, State, Method } from '@stencil/core'

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true,
})
export class MyComponent {
  /**
   * Show or hide the drawer
   */
  @State() open = true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we set up some internal data with the use of &lt;code&gt;@State&lt;/code&gt;. When changing the &lt;code&gt;State&lt;/code&gt; property it will cause the components to render again. With this, we are going to toggle a CSS class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, Host, h, State, Method } from '@stencil/core'

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true,
})
export class MyComponent {
  @State() open = true

  @Method()
  async toggleDrawer() {
    this.open = !this.open
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added a &lt;code&gt;@Method&lt;/code&gt; decorator and a method that toggles the property &lt;code&gt;open&lt;/code&gt;. Doing this allows us to call the &lt;code&gt;toggleDrawer&lt;/code&gt; method from the outside of the component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, Host, h, State, Method } from '@stencil/core'

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true,
})
export class MyComponent {
  @State() open = true

  @Method()
  async toggleDrawer() {
    this.open = !this.open
  }
  render() {
    return (
      &amp;lt;Host&amp;gt;
        &amp;lt;div class="drawer-container"&amp;gt;
          &amp;lt;div class={{ drawer: true, open: this.open }}&amp;gt;
            &amp;lt;div class="drawer-content"&amp;gt;
              &amp;lt;slot name="drawer-content" /&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class={{ content: true, left: !this.open }}&amp;gt;
            &amp;lt;slot name="content" /&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/Host&amp;gt;
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this last part, we defined the HTML template. Earlier in this article, I said we would toggle some CSS classes based on the &lt;code&gt;State&lt;/code&gt; property &lt;code&gt;open&lt;/code&gt;. Now you can see what I meant with this. When the property &lt;code&gt;open&lt;/code&gt; equals &lt;code&gt;true&lt;/code&gt; the &lt;code&gt;open&lt;/code&gt; class will be added to the div, similar behavior will be on the &lt;code&gt;content div&lt;/code&gt; as shown in the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/30e34bd5d2ae39fd76a9ebd3a1a7fdc5/43efa/outside_of_view.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dIbphBkq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://jefiozie.github.io/static/30e34bd5d2ae39fd76a9ebd3a1a7fdc5/b9e4f/outside_of_view.png" alt="Drawer outside of the view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now open the &lt;code&gt;my-component.css&lt;/code&gt; file and replace the code with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:host {
  display: block;
  width: 100%;
}

.drawer-container {
  position: relative;
  display: flex;
  box-sizing: border-box;
  overflow: hidden;
  border: 1px solid #ccc;
}

.drawer {
  padding-right: 20px;
  background: gray;
  border-right: 1px solid #ccc;
  position: relative;
  width: 280px;
  transform: translate3d(-100%, 0, 0);
  z-index: 2;
  box-sizing: border-box;
  will-change: transform;
}
.drawer-content {
  width: 100%;
  height: 100%;
}
.drawer.open {
  transform: none;
}

.content {
  margin-left: 0px;
  margin-right: 0px;
  flex-grow: 1;
  flex-shrink: 1;
  display: block;
  height: 100%;
  overflow: auto;
  will-change: contents;
}
.content.left {
  margin-left: -280px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will focus on the &lt;code&gt;open&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; behaviors. In our &lt;code&gt;drawer&lt;/code&gt; class we have defined &lt;code&gt;transform: translate3d(-100%, 0, 0);&lt;/code&gt;. This will place our div out of the view. When we append the &lt;code&gt;open&lt;/code&gt; class we will set &lt;code&gt;transform&lt;/code&gt; to &lt;code&gt;none&lt;/code&gt;, now we have our div back on the screen.&lt;/p&gt;

&lt;p&gt;Our other focus point is the &lt;code&gt;left&lt;/code&gt; class. Because the width of our drawer is fixed at &lt;code&gt;280px&lt;/code&gt; we can use &lt;code&gt;margin-left&lt;/code&gt; to “slide” to the left when this class is appended.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Now that we have all the parts together we have to do one more thing. When we created our new project &lt;code&gt;Stencil&lt;/code&gt; provides us with an &lt;code&gt;index.html&lt;/code&gt; within this file it has already created all thing for the “example” component.&lt;/p&gt;

&lt;p&gt;Yet because we made changes we should reflect these changes in the &lt;code&gt;index.html&lt;/code&gt; file. To speed things up copy the code below and replace this in your &lt;code&gt;index.html&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;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html dir="ltr" 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, minimum-scale=1.0, maximum-scale=5.0"
    /&amp;gt;
    &amp;lt;title&amp;gt;Stencil Component Starter&amp;lt;/title&amp;gt;

    &amp;lt;script type="module" src="/build/example-drawer.esm.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script nomodule src="/build/example-drawer.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
      html,
      body {
        height: 100%;
        margin: 0;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;my-component&amp;gt;
      &amp;lt;ul slot="drawer-content"&amp;gt;
        &amp;lt;li&amp;gt;Menu 1&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;Menu 2&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;Menu 3&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;Menu 4&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;Menu 5&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
      &amp;lt;div slot="content"&amp;gt;
        Main
      &amp;lt;/div&amp;gt;
    &amp;lt;/my-component&amp;gt;
    &amp;lt;button id="toggleBtn"&amp;gt;Toggle drawer&amp;lt;/button&amp;gt;
  &amp;lt;/body&amp;gt;
  &amp;lt;script&amp;gt;
    const btn = document.getElementById("toggleBtn");
    const = document.querySelector('my-component');
       btn.addEventListener("click", () =&amp;gt; {
         drawer.toggleDrawer();
       });
  &amp;lt;/script&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What did we change? We have added an &lt;em&gt;unsorted list&lt;/em&gt; that will be placed in slot: &lt;code&gt;drawer-content&lt;/code&gt; and we have a &lt;code&gt;div&lt;/code&gt; that will be placed in slot: &lt;code&gt;content&lt;/code&gt;. To toggle our drawer we have added a simple button that &lt;code&gt;on-click&lt;/code&gt; will call the &lt;code&gt;toggleDrawer&lt;/code&gt; method on our &lt;code&gt;drawer&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;So what did we do in summary? We have created a simple drawer that slides out of the view. We have created a drawer with basic styling that can be enhanced to your own needs. The great thing is it that we can use this drawer in &lt;strong&gt;ANY&lt;/strong&gt; frontend application that is using Vanilla JS, Angular, React or Vue, etc.&lt;/p&gt;

&lt;p&gt;To see the full source have a look at &lt;a href="https://github.com/Jefiozie/stencil-drawer"&gt;this repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this article inspired you, gave you some insights on how to build a component with the use of &lt;strong&gt;Stencil&lt;/strong&gt; and have had some fun!😁&lt;/p&gt;

&lt;h2&gt;
  
  
  A Thank you
&lt;/h2&gt;

&lt;p&gt;At last, a thank you to some people how reviewed my article&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/wesgrimes"&gt;Wes Grimes (@wesgrimes)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/romulocintra"&gt;Romulo Cintra (@romulocintra)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>stencil</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Hi I'm Jeffrey, Ask Me Anything</title>
      <dc:creator>Jeffrey Bosch</dc:creator>
      <pubDate>Sat, 27 Apr 2019 09:26:35 +0000</pubDate>
      <link>https://dev.to/jefiozie/hi-i-m-jeffrey-ask-me-anything-12n7</link>
      <guid>https://dev.to/jefiozie/hi-i-m-jeffrey-ask-me-anything-12n7</guid>
      <description>&lt;p&gt;My name is Jeffrey and I’m an frontend developer working at a digital signature solution.&lt;/p&gt;

&lt;p&gt;My daily job is frontend related; working in Angular, React and some vanilla Javascript. Backend I'm working in PHP and .NET C# these days.&lt;/p&gt;

&lt;p&gt;Next to my daily job I work on an open source project called &lt;a href="https://xlayers.app"&gt;xLayers&lt;/a&gt;. With this project we are trying to bridge the gap between designers and developer. This project is developed with Angular and we are looking for contributions from the community. &lt;/p&gt;

&lt;p&gt;My knowledge is mainly around frontend(mostly angular related) and cloud related things. You can ask my anything around these topics, checkout &lt;a href="https://jefiozie.github.io"&gt;my personal blog&lt;/a&gt; or just say 👋🏻 "Hi"&lt;/p&gt;

</description>
      <category>angular</category>
      <category>frontend</category>
      <category>ama</category>
      <category>design</category>
    </item>
  </channel>
</rss>
