<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[FL0 Engineering - Medium]]></title>
        <description><![CDATA[FL0 is a cloud development and deployment platform optimized for backend services. You write the services, and FL0 creates, deploys and scales the containers running them. - Medium]]></description>
        <link>https://medium.com/fl0-engineering?source=rss----ae7966bd51e3---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>FL0 Engineering - Medium</title>
            <link>https://medium.com/fl0-engineering?source=rss----ae7966bd51e3---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 11 May 2026 16:54:36 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/fl0-engineering" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Building a User-Friendly URL Shortener Using Spring Boot, Postgres, and FL0]]></title>
            <link>https://medium.com/fl0-engineering/building-a-user-friendly-url-shortener-using-spring-boot-postgres-and-fl0-d8bac14b08a5?source=rss----ae7966bd51e3---4</link>
            <guid isPermaLink="false">https://medium.com/p/d8bac14b08a5</guid>
            <category><![CDATA[hosting]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[spring-boot]]></category>
            <dc:creator><![CDATA[Dale Brett]]></dc:creator>
            <pubDate>Thu, 27 Jul 2023 00:29:54 GMT</pubDate>
            <atom:updated>2023-07-27T00:30:57.801Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lYYOv1pSXy1SrSWGG5brTA.png" /></figure><h3>TL;DR</h3><p>In this tutorial, we will create a user-friendly URL shortener using SpringBoot and Postgres, deployed on FL0 for easy management and control. 🔗✨</p><h3>Introduction</h3><p>Nowadays, URLs have become an essential part of our workflow. However, long and complex URLs can be difficult to share or work with.</p><p>In this tutorial, we will build a simple-to-use URL shortener that provides a convenient way to transform long URLs into shorter, more manageable links in just one click! 🧑‍💻</p><p>We would be using Spring Boot to build our backend, Postgres as our database, both deployed simply using FL0. Then we would go ahead and build the user interface in the form of a simple Chrome Extension, which could call our APIs!</p><p>Here’s some internet humor before we get started:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-ilbzCb3OAgTPjqxufYDvA.png" /></figure><h3>Overview</h3><p>Before we dive into the code, let’s take a moment to understand the high-level overview of our project.</p><p>Our URL shortener will be a Chrome extension designed to provide a seamless and delightful experience for users. Let’s walk through the user journey to get a clear picture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*af7u2YhgwPEsXPqz2HZmoQ.png" /></figure><ol><li><strong>Navigation:</strong> The user opens Chrome and visits the webpage he/she wants to shorten using our installed extension.</li><li><strong>URL Shortening:</strong> Upon clicking the extension, it fetches the active tab’s URL and sends it to our shortening service. This service is a Spring Boot application hosted on FL0, which processes the URL and generates a unique path.</li><li><strong>URL Construction and Copying:</strong> The extension takes this unique path, constructs the full shortened URL on the front end, displays it to the user, and it can be copied with a single click.</li></ol><p>Here’s a high-level diagram for better understanding:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W47Zzqn03sMmd7HMtK4s9w.png" /></figure><h3>Step 0: Setting Up the Spring Boot Project</h3><p>Before we begin, we need to make sure we have the following tools installed:</p><ul><li><strong>Code Editor</strong>: In this tutorial, we’ll be using <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a>.</li><li><strong>JDK 17</strong>: we need to have JDK 17 installed.</li><li><strong>Docker Desktop (on Mac/Windows)</strong>: We’ll utilize <a href="https://www.docker.com/">Docker</a> to containerize our application and simplify deployment.</li></ul><p>Now we would go ahead and set up our new Spring Boot project using <a href="https://start.spring.io/">Spring Initializr</a> 🌱</p><ol><li>Let’s visit <a href="http://start.spring.io/">start.spring.io</a>. This is the <a href="https://start.spring.io/">Spring Initializr</a>, a web-based tool that helps in creating Spring Boot projects quickly.</li><li>Now, we would need to configure our settings as follows:</li></ol><p><strong>Project Metadata</strong>: Specify the project metadata such as group, artifact, and version.</p><p><strong>Dependencies</strong>: Add the following dependencies:</p><ul><li>Spring Web: To build RESTful APIs and handle HTTP requests.</li><li>Spring Data JPA: For working with databases using Object-Relational Mapping (ORM).</li><li>PostgreSQL Driver: To connect our application with a PostgreSQL database.</li><li>Lombok: A library that simplifies Java code by reducing boilerplate.</li></ul><p>3. We will select the latest stable Java version (Java 17), choose the project packaging as JAR, and select Gradle as the build tool. 🧑‍💻</p><p>4. Click on the “Generate” button to download a zip file containing the project structure and necessary files 🗂️ as shown👇</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FFEOI5DcTAbCnxL5eMvCKA.gif" /><figcaption>Spring Initializr</figcaption></figure><h4>Setting Up the Project</h4><ol><li>We will extract the downloaded zip file to a preferred location.</li><li>Now we will open our code editor (IntelliJ IDEA, in our case) and import the project by selecting the extracted folder as the project directory.</li><li>Once the project is loaded, we would need to download the project dependencies specified in the build.gradle file. This can be done automatically in IntelliJ IDEA by clicking on the Gradle toolbar located on the right side of the editor and selecting the &quot;Refresh&quot; button.</li><li>Verify that the dependencies are successfully downloaded by checking the Gradle Console for any errors or warnings.</li></ol><h3>Step 1: Database and Config</h3><p>In this step, we’ll configure the necessary files and set up the database connection for our user-friendly URL shortener. Let’s get started with the configuration setup!</p><h4>Postgresql Database Setup</h4><p>To run our app locally, we would also need postgresql running. So, lets set it up quickly. We create a new folder src/main/resources/local and create a new file local-postgre-docker-compose.yml</p><pre>version: &#39;3&#39;<br><br>services:<br>  postgres:<br>    image: postgres<br>    restart: always<br>    ports:<br>      - 5432:5432<br>    environment:<br>      POSTGRES_DB: url_shortener<br>      POSTGRES_USER: user123<br>      POSTGRES_PASSWORD: pass123<br>    volumes:<br>      - db_data:/var/lib/postgresql/data<br>volumes:<br>  db_data:</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yEBYD0Cduyz4uaX0BmM8eA.gif" /></figure><p>To start our DB, in terminal navigate to the folder containing this file and run the command:</p><pre>docker-compose -f local-postgre-docker-compose.yml up -d</pre><p>This will start postgresql database as a docker container and we will be able to connect to it on localhost:5432 using the credentials as mentioned in the above docker file</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i8cSL-nB_fZ4iBLPPdyo7g.gif" /></figure><h4>Configuring Files and Database Connection</h4><ol><li>Open the application.yml file located in the src/main/resources directory. This file allows us to define properties for our application.</li><li>Add the following properties:</li></ol><pre>server:<br>  port: 8080<br><br>spring:<br>  datasource:<br>    url: jdbc:postgresql://${DB_HOST_NAME:localhost}:${DB_PORT:5432}/${DB_NAME:url_shortener}<br>    username: ${DB_USERNAME:user123}<br>    password: ${DB_PASSWORD:pass123}<br>  jpa:<br>    hibernate:<br>      ddl-auto: update<br><br>short-url:<br>  allowed-characters: ${ALLOWED_CHARS:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789}<br>  key-length: ${KEY_LENGTH:6}</pre><ul><li>The variables written as ${ENV_VARIABLE_NAME:default_value} can be set using environment variables while deployment.</li><li>For local development we can use the default values.</li><li>server.port: The port on which our application will run.</li><li>spring.datasource.url: The URL for connecting to our PostgreSQL database.</li><li>spring.datasource.username and spring.datasource.password: PostgreSQL database credentials.</li><li>short-url.allowed-characters: The characters allowed in the generated keys. Feel free to modify or expand the character set if desired.</li><li>short-url.key-length: The length of the generated keys for short url. We will be using length of 6 characters as key.</li></ul><p>Now we can run our application using below command:</p><pre>./gradlew bootRun</pre><h4>ShortUrlConfig Class</h4><p>To centralize our configuration properties and make them easily accessible, let’s create a ShortUrlConfig class. This class will be annotated with @ConfigurationProperties(prefix=&quot;short-url&quot;) to bind the properties from the application.yml file to the corresponding fields in our class. Here&#39;s an example:</p><pre>package com.fl0.urlshortener.config;<br><br>import lombok.Getter;<br>import lombok.Setter;<br>import org.springframework.boot.context.properties.ConfigurationProperties;<br>@ConfigurationProperties(prefix = &quot;short-url&quot;)<br>@Getter<br>@Setter<br>public class ShortUrlConfig {<br>    private String allowedCharacters;<br>    private int keyLength;<br>}</pre><p>This will require us to add @ConfigurationPropertiesScan on top of the main application class. So, lets add it to our UrlshortenerApplication :</p><pre>package com.fl0.urlshortener;<br><br>import org.springframework.boot.SpringApplication;<br>import org.springframework.boot.autoconfigure.SpringBootApplication;<br>import org.springframework.boot.context.properties.ConfigurationPropertiesScan;<br>@SpringBootApplication<br>**@ConfigurationPropertiesScan**<br>public class UrlshortenerApplication {<br> public static void main(String[] args) {<br>  SpringApplication.run(UrlshortenerApplication.class, args);<br> }<br>}</pre><p>Now that we have our files configured and the database connection established, it’s time to move on to creating the necessary models and repositories.</p><p>In this step, we’ll configure the necessary files and set up the database connection for our user-friendly URL shortener. Let’s get started with the configuration setup!</p><h4>Postgresql Database Setup</h4><p>To run our app locally, we would also need postgresql running. So, lets set it up quickly. We create a new folder src/main/resources/local and create a new file local-postgre-docker-compose.yml</p><pre>version: &#39;3&#39;<br><br>services:<br>  postgres:<br>    image: postgres<br>    restart: always<br>    ports:<br>      - 5432:5432<br>    environment:<br>      POSTGRES_DB: url_shortener<br>      POSTGRES_USER: user123<br>      POSTGRES_PASSWORD: pass123<br>    volumes:<br>      - db_data:/var/lib/postgresql/data<br>volumes:<br>  db_data:</pre><p>To start our DB, in terminal navigate to the folder containing this file and run the command:</p><pre>docker-compose -f local-postgre-docker-compose.yml up -d</pre><p>This will start postgresql database as a docker container and we will be able to connect to it on localhost:5432 using the credentials as mentioned in the above docker file</p><p>Configuring Files and Database Connection</p><ol><li>Open the application.yml file located in the src/main/resources directory. This file allows us to define properties for our application.</li><li>Add the following properties:</li></ol><pre>server:<br>  port: 8080<br><br>spring:<br>  datasource:<br>    url: jdbc:postgresql://${DB_HOST_NAME:localhost}:${DB_PORT:5432}/${DB_NAME:url_shortener}<br>    username: ${DB_USERNAME:user123}<br>    password: ${DB_PASSWORD:pass123}<br>  jpa:<br>    hibernate:<br>      ddl-auto: update<br><br>short-url:<br>  allowed-characters: ${ALLOWED_CHARS:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789}<br>  key-length: ${KEY_LENGTH:6}</pre><ul><li>The variables written as ${ENV_VARIABLE_NAME:default_value} can be set using environment variables while deployment. If not set then the default value will be used.</li><li>For local development we can use the default values.</li><li>server.port: The port on which our application will run.</li><li>spring.datasource.url: The URL for connecting to our PostgreSQL database.</li><li>spring.datasource.username and spring.datasource.password: PostgreSQL database credentials.</li><li>short-url.allowed-characters: The characters allowed in the generated keys. Feel free to modify or expand the character set if desired.</li><li>short-url.key-length: The length of the generated keys for short url. We will be using length of 6 characters as key.</li></ul><p>Now we can run our application using below command:</p><pre>./gradlew bootRun</pre><h4>ShortUrlConfig Class</h4><p>To centralize our configuration properties and make them easily accessible, let’s create a ShortUrlConfig class. This class will be annotated with @ConfigurationProperties(prefix=&quot;short-url&quot;) to bind the properties from the application.yml file to the corresponding fields in our class. Here&#39;s an example:</p><pre>package com.fl0.urlshortener.config;<br><br>import lombok.Getter;<br>import lombok.Setter;<br>import org.springframework.boot.context.properties.ConfigurationProperties;<br>@ConfigurationProperties(prefix = &quot;short-url&quot;)<br>@Getter<br>@Setter<br>public class ShortUrlConfig {<br>    private String allowedCharacters;<br>    private int keyLength;<br>}</pre><p>This will require us to add @ConfigurationPropertiesScan on top of the main application class. So, lets add it to our UrlshortenerApplication :</p><pre>package com.fl0.urlshortener;<br><br>import org.springframework.boot.SpringApplication;<br>import org.springframework.boot.autoconfigure.SpringBootApplication;<br>import org.springframework.boot.context.properties.ConfigurationPropertiesScan;<br>@SpringBootApplication<br>**@ConfigurationPropertiesScan**<br>public class UrlshortenerApplication {<br> public static void main(String[] args) {<br>  SpringApplication.run(UrlshortenerApplication.class, args);<br> }<br>}</pre><p>Now that we have our files configured and the database connection established, it’s time to move on to creating the necessary models and repositories.</p><h3>Step 2: Creating Entity and Repository</h3><p>Next we’ll define and create the necessary models and repositories for our URL shortener. The models will represent the table structure of our shortened URLs, and the repositories will handle the database operations. Let’s dive in!</p><h4>ShortUrl Entity</h4><ol><li>Create a new Java class named ShortUrlEntity in a new package com.fl0.urlshortener.entity</li><li>Define the fields for the ShortUrlEntity entity class:</li></ol><pre>package com.fl0.urlshortener.entity;<br><br>import jakarta.persistence.*;<br>import lombok.*;<br><br>@Entity<br>@Data<br>@Builder<br>@AllArgsConstructor<br>@NoArgsConstructor<br>@Table(name = &quot;urls&quot;)<br>public class ShortUrlEntity {<br>    @Id<br>    @Column(name = &quot;id&quot;, nullable = false)<br>    @GeneratedValue(strategy = GenerationType.IDENTITY)<br>    private Long id;<br><br>    @Column(unique = true)<br>    private String key;<br><br>    @Column(nullable = false, columnDefinition = &quot;TEXT&quot;)<br>    private String fullUrl;<br><br>    @Column(nullable = false)<br>    private Long clickCount;<br>}</pre><ol><li>The ShortUrlEntity entity represents a shortened URL and is annotated with @Entity to indicate that it&#39;s a JPA entity. The @Table annotation specifies the name of the table in the database.</li><li>The entity has the following fields:</li></ol><ul><li>id: The primary key generated automatically by the database.</li><li>key: The unique key representing the shortened URL.</li><li>fullUrl: The original full URL that was shortened.</li><li>clickCount: The number of times the shortened URL has been clicked.</li></ul><h4>ShortUrlRepository</h4><ol><li>Create a new Java interface named ShortUrlRepository in a new package com.fl0.urlshortener.repository</li><li>Extend the JpaRepository interface and define custom methods for the repository:</li></ol><pre>package com.fl0.urlshortener.repository;<br><br>import com.fl0.urlshortener.entity.ShortUrlEntity;<br>import org.springframework.data.jpa.repository.JpaRepository;<br><br>public interface ShortUrlRepository extends JpaRepository&lt;ShortUrlEntity, Long&gt; {<br>    ShortUrlEntity findByKey(String key);<br>    ShortUrlEntity findByFullUrl(String fullUrl);<br>}</pre><ol><li>The ShortUrlRepository extends the JpaRepository interface provided by Spring Data JPA. It enables us to perform CRUD operations on the ShortUrlEntity easily.</li><li>We will also define two custom methods:</li></ol><ul><li>findByKey: Retrieves a ShortUrlEntity entity based on the given key.</li><li>findByFullUrl: Retrieves a ShortUrlEntity entity based on the given full URL.</li></ul><p>These methods will be used in our service layer to retrieve and manipulate data.</p><h3>Step 3: Creating DTOs, Utility and Service classes</h3><p>Now we’ll create the data transfer objects (DTOs), a utility class and service layer. The DTOs will facilitate data transfer, the utility class will provide helpful methods and the service will handle the business logic. Let’s proceed!</p><h4>DTOs (Data Transfer Objects)</h4><ol><li>Let’s create a new Java class named ShortUrlRequest in the com.example.urlshortener.dto package.</li><li>Now we will define the fields for the ShortUrlRequest class:</li></ol><pre>package com.fl0.urlshortener.dto;<br><br>import lombok.Getter;<br>import lombok.Setter;<br><br>@Getter<br>@Setter<br>public class ShortUrlRequest {<br>    private String url;<br>}</pre><ol><li>The ShortUrlRequest DTO represents a request to create a shortened URL. It has a single field, url.</li><li>We will create another Java class named ShortUrlResponse in the same package.</li><li>Now we will define the fields for the ShortUrlResponse class:</li></ol><pre>package com.fl0.urlshortener.dto;<br><br>import lombok.Builder;<br>import lombok.Getter;<br>import lombok.Setter;<br><br>@Getter<br>@Setter<br>@Builder<br>public class ShortUrlResponse {<br>    private String key;<br>}</pre><ol><li>The ShortUrlResponse DTO represents the response after creating a shortened URL. It has a single field: key, which holds the unique key as a response.</li></ol><h4>ShortUrlUtil</h4><p>We will create a new Java class named ShortUrlUtil in the com.fl0.urlshortener.util package and add the @Component annotation to the ShortUrlUtil class to make it a Spring bean:</p><pre>package com.fl0.urlshortener.util;<br><br>import com.fl0.urlshortener.config.ShortUrlConfig;<br>import org.springframework.beans.factory.annotation.Autowired;<br>import org.springframework.stereotype.Component;<br><br>import java.util.Random;<br><br>@Component<br>public class ShortUrlUtil {<br><br>    private final ShortUrlConfig config;<br><br>    @Autowired<br>    public ShortUrlUtil(ShortUrlConfig config) {<br>        this.config = config;<br>    }<br><br>    public String generateUniqueKey() {<br>        int keyLength = config.getKeyLength();<br>        String allowedCharacters = config.getAllowedCharacters();<br><br>        StringBuilder keyBuilder = new StringBuilder();<br>        Random random = new Random();<br><br>        for (int i = 0; i &lt; keyLength; i++) {<br>            int randomIndex = random.nextInt(allowedCharacters.length());<br>            keyBuilder.append(allowedCharacters.charAt(randomIndex));<br>        }<br><br>        return keyBuilder.toString();<br>    }<br>}</pre><p>The ShortUrlUtil class is annotated with @Component to make it a Spring bean. It is also injected with the ShortUrlConfig using constructor injection.</p><p>The ShortUrlUtil class provides a single method:</p><ul><li>generateUniqueKey(): Generates a unique key based on the specified length and allowed characters from the configuration.</li></ul><p>This method will be used in the service layer.</p><h4>UrlShortenerService</h4><ol><li>Create a new Java class named UrlShortenerService in the com.fl0.urlshortener.service package.</li><li>Add the following methods to handle URL shortening and retrieval:</li></ol><pre>package com.fl0.urlshortener.service;<br><br>import com.fl0.urlshortener.dto.ShortUrlRequest;<br>import com.fl0.urlshortener.dto.ShortUrlResponse;<br>import com.fl0.urlshortener.entity.ShortUrlEntity;<br>import com.fl0.urlshortener.repository.ShortUrlRepository;<br>import com.fl0.urlshortener.util.ShortUrlUtil;<br>import lombok.RequiredArgsConstructor;<br>import org.springframework.stereotype.Service;<br>import org.springframework.web.servlet.view.RedirectView;<br><br>@Service<br>@RequiredArgsConstructor<br>public class UrlShortenerService {<br><br>    private final ShortUrlRepository repository;<br>    private final ShortUrlUtil util;<br><br>    public ShortUrlResponse createShortUrl(ShortUrlRequest request) {<br>        String fullUrl = request.getUrl();<br><br>        ShortUrlEntity existingShortUrl = repository.findByFullUrl(fullUrl);<br><br>        if (existingShortUrl != null) {<br>            return ShortUrlResponse.builder().key(existingShortUrl.getKey()).build();<br>        } else {<br>            String newKey = util.generateUniqueKey();<br>            ShortUrlEntity newEntity = ShortUrlEntity.builder()<br>                    .key(newKey).fullUrl(fullUrl).clickCount(0L)<br>                    .build();<br>            repository.save(newEntity);<br>            return ShortUrlResponse.builder().key(newKey).build();<br>        }<br>    }<br><br>    public RedirectView getFullUrl(String key) {<br>        ShortUrlEntity entityInDb = repository.findByKey(key);<br>        entityInDb.setClickCount(entityInDb.getClickCount() + 1);<br>        repository.save(entityInDb);<br>        return new RedirectView(entityInDb.getFullUrl());<br>    }<br>} </pre><p>The UrlShortenerService class handles the business logic of URL shortening and retrieval. It relies on the ShortUrlRepository for database operations and the ShortUrlUtil for generating unique keys.</p><p>The createShortUrl method checks if the URL already exists in the database.</p><p>If it exists, we get the key (the path) of the URL from the database.</p><p>Otherwise, it generates a new key, saves it along with the URL in the database, and returns the newly generated key.</p><p>The getFullUrl method retrieves the original full URL based on the provided key.</p><p>It finds the key in the database, increments the click count, saves the changes, and redirects to the original full URL.</p><p>Our application is now equipped with the necessary components to handle the creation and retrieval of shortened URLs.</p><h3>Step 5: Enabling Cross-Origin Resource Sharing (CORS)</h3><p>To allow requests from the Chrome extension to our backend API, we need to configure Cross-Origin Resource Sharing (CORS). In this step, we’ll create a CorsConfig class in our Spring Boot application to handle CORS configuration.</p><ol><li>We create a new Java class named CorsConfig in the com.fl0.urlshortener.config package.</li></ol><pre>package com.fl0.urlshortener.config;<br><br>import org.springframework.context.annotation.Configuration;<br>import org.springframework.web.servlet.config.annotation.CorsRegistry;<br>import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;<br><br>@Configuration<br>public class CorsConfig implements WebMvcConfigurer {<br><br>    @Override<br>    public void addCorsMappings(CorsRegistry registry) {<br>        registry.addMapping(&quot;/**&quot;)<br>                .allowedOrigins(&quot;*&quot;)<br>                .allowedMethods(&quot;GET&quot;, &quot;POST&quot;);<br>    }<br>}</pre><ol><li>The CorsConfig class implements the WebMvcConfigurer interface, allowing us to customize the CORS configuration for our application.</li><li>With this configuration, our backend will allow requests from the Chrome extension, enabling communication between the extension and the API.</li><li>Save the CorsConfig class.</li></ol><p>We have successfully built the CorsConfig class to handle CORS configuration in our Spring Boot application. This will ensure that our backend API can accept requests from our Chrome extension without any CORS-related issues. ✅</p><p>Next, let’s move on to build our Chrome extension. 👨🏻‍💻</p><h3>Step 6: Building the Chrome Extension</h3><p>In this step, we’ll create a custom Chrome extension for our URL shortener. The extension will provide a convenient way for users to shorten URLs directly from their browser. Let’s dive into the world of Chrome extension development!</p><ol><li>We will create a new project named url-shortener-extension.</li></ol><p>manifest.json</p><pre>{<br>  &quot;manifest_version&quot;: 3,<br>  &quot;name&quot;: &quot;URL Shortener&quot;,<br>  &quot;version&quot;: &quot;1.0&quot;,<br>  &quot;description&quot;: &quot;Shortens URLs with ease!&quot;,<br>  &quot;background&quot;: {<br>    &quot;service_worker&quot;: &quot;background.js&quot;<br>  },<br>  &quot;action&quot;: {<br>    &quot;default_popup&quot;: &quot;popup.html&quot;,<br>    &quot;default_icon&quot;: &quot;icon.png&quot;<br>  },<br>  &quot;permissions&quot;: [&quot;activeTab&quot;],<br>  &quot;icons&quot;: {<br>    &quot;16&quot;: &quot;icon.png&quot;,<br>    &quot;48&quot;: &quot;icon.png&quot;,<br>    &quot;128&quot;: &quot;icon.png&quot;<br>  }<br>}</pre><p>popup.html</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;popup.css&quot;&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>&lt;div class=&quot;container&quot;&gt;<br>    &lt;button id=&quot;copyBtn&quot; disabled&gt;Copy url&lt;/button&gt;<br>&lt;/div&gt;<br>&lt;script src=&quot;popup.js&quot;&gt;&lt;/script&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p>popup.css</p><pre><br>body {<br>    background-color: #e6f7ff;<br>    margin: 5px;<br>    font-family: Arial, sans-serif;<br>}<br><br>#copyBtn {<br>    padding: 5px 10px;<br>    background-color: #1890ff;<br>    border: none;<br>    color: white;<br>    font-size: 1.2em;<br>    transition: background-color 0.5s ease;<br>    text-align: center;<br>    white-space: nowrap;<br>    text-overflow: ellipsis;<br>    cursor: pointer;<br>}</pre><p>popup.js</p><pre>let shortenedUrl;<br><br>window.onload = function() {<br>    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {<br>        chrome.runtime.sendMessage(<br>            {message: &#39;fetchUrl&#39;, url: tabs[0].url},<br>            function(response) {<br>                shortenedUrl = response.url;<br>                document.getElementById(&#39;copyBtn&#39;).disabled = false;<br>            }<br>        );<br>    });javaj<br>};<br>document.getElementById(&#39;copyBtn&#39;).addEventListener(&#39;click&#39;, function() {<br>    navigator.clipboard.writeText(shortenedUrl).then(function() {<br>        console.log(&#39;Copying to clipboard was successful!&#39;);<br>        const btn = document.getElementById(&#39;copyBtn&#39;);<br>        btn.innerText = &#39;Success!&#39;;<br>        btn.style.backgroundColor = &#39;#52c41a&#39;;<br>        // Close the popup after 2 seconds<br>        setTimeout(window.close, 2000);<br>    }, function(err) {<br>        console.error(&#39;Could not copy text: &#39;, err);<br>    });<br>});</pre><p>background.js</p><pre>const backendUrl = &#39;https://localhost:8080&#39;;<br>// Don&#39;t forget to replace this url with the actual backend url after deployment<br><br>chrome.runtime.onMessage.addListener((request, sender, sendResponse) =&gt; {<br>    if (request.message === &#39;fetchUrl&#39;) {<br>        fetch(backendUrl + &#39;createUrl&#39;, {<br>            method: &#39;POST&#39;,<br>            headers: {<br>                &#39;Content-Type&#39;: &#39;application/json&#39;<br>            },<br>            body: JSON.stringify({ url: request.url })<br>        })<br>            .then(response =&gt; response.json())<br>            .then(data =&gt; {<br>                sendResponse({url: backendUrl + data.key});<br>            })<br>            .catch(err =&gt; console.error(&#39;Error: &#39;, err));<br>        return true;<br>    }<br>});</pre><p>Now lets load the extension in our Chrome browser by following these steps:</p><ol><li>Open the Chrome browser.</li><li>Go to chrome://extensions/.</li><li>Enable the “Developer mode” toggle on the top right corner.</li><li>Click on the “Load unpacked” button.</li><li>Select the url-shortener-extension directory.</li><li>The extension will be loaded and available in the Chrome browser.</li></ol><p>We’ve successfully built the Chrome extension for our URL shortener.</p><p>Now let’s go ahead and dockerize our application.</p><h3>Step 7: Dockerizing the App</h3><p>Now we’ll dockerize our application, making it easy to deploy and run in a containerized environment. Let’s get started!</p><h4>Dockerfile</h4><p>We create a new file named Dockerfile in the root directory of our project.</p><p>Here we specify the instructions for building the Docker image of our app for deployment.</p><pre># Start with a base image containing Java runtime (AdoptOpenJDK)<br>FROM openjdk:17-jdk-slim AS build<br><br># Set the working directory in the image to &quot;/app&quot;<br>WORKDIR /app<br><br># Copy the Gradle executable to the image<br>COPY gradlew ./<br><br># Copy the &#39;gradle&#39; folder to the image<br>COPY gradle ./gradle<br><br># Give permission to execute the gradle script<br>RUN chmod +x ./gradlew<br><br># Copy the rest of the application source code<br>COPY . .<br><br># Use Gradle to build the application<br>RUN sh ./gradlew build<br><br># Set up a second stage, which will only keep the compiled application and not the build tools and source code<br>FROM openjdk:17-jdk-slim<br><br># Set the working directory to &#39;/app&#39;<br>WORKDIR /app<br><br># Copy the jar file from the first stage<br>COPY --from=build /app/build/libs/*.jar app.jar<br><br># Set the startup command to execute the jar<br>CMD [&quot;java&quot;, &quot;-jar&quot;, &quot;/app/app.jar&quot;]</pre><h4>Docker Compose</h4><p>Now we will create a docker-compose in the project’s root directory.</p><pre>version: &#39;3.8&#39;<br>services:<br>  url-shortener-backend:<br>    build:<br>      context: .<br>      dockerfile: Dockerfile<br>    ports:<br>      - &quot;8080:8080&quot;</pre><p>This docker-compose.yml file defines the service url-shortener-backend . The service is based on the configuration in the Dockerfile. It maps the container&#39;s port 8080 to the host&#39;s port 8080.</p><p>Let’s now navigate to the next section and explore hosting our backend and database with FL0!</p><h3>Step 8: Hosting our Backend and DB with FL0</h3><p>Now we would go ahead and host our application with the help of <a href="https://www.fl0.com/">FL0</a></p><h4>Setting Up FL0 Database</h4><ol><li>In the FL0 dashboard, we would need to create a new project.</li><li>We need to click on “Create a Postgres database”</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rtbyS9DE5jgIxX2GuDX3Dw.png" /></figure><p>3. Once the database is created, FL0 will provide us with the necessary database connection details, including the connection URL, username, and password.</p><p>4. Add Database Connection Credentials as Environment Variables in <strong>fl0-url-shortener-backend</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vTBZr3dMym7DOV14Dv0tQg.gif" /></figure><p>And…Done ✅</p><p>Our project is hosted on FL0 <strong>successfully</strong>.</p><h3>Conclusion</h3><p>In this tutorial successfully built a link shortener chrome extension along with it’s backend and database and hosted it using FL0. 🎉</p><p>You may find the repository link here <a href="https://github.com/dalefl0/fl0-url-shortener-backend">https://github.com/dalefl0/fl0-url-shortener-backend</a>.</p><p>Moreover, here’s the link for the Chrome Extension if you want to use it yourself <a href="https://github.com/dalefl0/URL-Shortener-Chrome-Extension">https://github.com/dalefl0/URL-Shortener-Chrome-Extension</a></p><p>You may visit <a href="https://fl0.com/">https://fl0.com/</a> to start building and deploying your applications! 🚀</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*kbUTNQ5Pw-esY0-8yjb8JQ.gif" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d8bac14b08a5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fl0-engineering/building-a-user-friendly-url-shortener-using-spring-boot-postgres-and-fl0-d8bac14b08a5">Building a User-Friendly URL Shortener Using Spring Boot, Postgres, and FL0</a> was originally published in <a href="https://medium.com/fl0-engineering">FL0 Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Seamless Payment Interfaces with Stripe and FL0]]></title>
            <link>https://medium.com/fl0-engineering/building-seamless-payment-interfaces-with-stripe-and-fl0-c3558a4e8033?source=rss----ae7966bd51e3---4</link>
            <guid isPermaLink="false">https://medium.com/p/c3558a4e8033</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[stripe-integration]]></category>
            <category><![CDATA[payment-gateway]]></category>
            <dc:creator><![CDATA[Dale Brett]]></dc:creator>
            <pubDate>Wed, 19 Jul 2023 21:30:53 GMT</pubDate>
            <atom:updated>2023-07-24T06:09:54.100Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*kQcd575ck908Vxp6ZTgiMw.png" /><figcaption>Building Seamless Payment Interfaces with Stripe and FL0</figcaption></figure><h3>TLDR;</h3><p>In this tutorial, we’ll explore how to seamlessly integrate Stripe payment gateway into our full-stack applications, and effortlessly host them on FL0. 🚀</p><h3>Introduction</h3><p>Whether it be an e-commerce or a SaaS application, payment gateways are a central component of our projects. 💳</p><p>In this guide, we will explore how to simplify these integrations, specifically focusing on <a href="https://stripe.com/docs/payments/checkout">Stripe Checkout</a> for online payment processing.</p><p>Stripe’s developer-friendly API ensures secure and efficient transactions while cutting down on our development time.</p><p>Just for example, we have taken the case of a SaaS applications payment page.</p><p>We would be using NodeJs for the backend and Postgres as our database. On the frontend we are using ReactJs with vite.</p><p>Later we would go ahead and effortlessly host our project on <a href="https://fl0.com/">FL0</a>. ⬆️</p><p>So, let’s start with a pinch of humor:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/0*X4v1n62n5Ns4WD3x.jpg" /></figure><h3>Overview</h3><p>🧑‍💻 In this tutorial, we will create a simple demo application where a user could sign up, select their plan, and checkout with their credit card.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-O0-iXr2ry4Lvwpb.png" /><figcaption>User Journey</figcaption></figure><p>For this, we would need to create 2 separate repositories, one for our backend and another one for frontend.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*GmL_rg3yBW9L3fks.png" /><figcaption>High Level Overview</figcaption></figure><h3>Folder Structure</h3><p>🗂️ Here’s how both of our folder structures would look like, just for reference:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/966/0*3nxztXwDUq02Uuxm.png" /></figure><p>Now, let’s get started.</p><h3>Step 1: Setting Up the Backend</h3><p>For the sake of efficiency, in this tutorial, we’ll leverage the “<a href="https://github.com/fl0zone/blog-express-pg-sequelize">fl0zone/blog-express-pg-sequelize</a>” template.</p><p>Then we would remove any files or folders not important for our project. 🧑‍💻</p><p>For a more comprehensive understanding of the tutorial, you may want to refer to this blog post:</p><p><a href="https://blog.fl0.com/building-a-software-startup-on-fl0-part-1-f0624174e738">https://blog.fl0.com/building-a-software-startup-on-fl0-part-1-f0624174e738</a></p><p>Our template encapsulates a basic Node.js application and a dockerized PostgreSQL database.</p><p>Here is the corresponding docker-compose.yaml file for our setup 🐳:</p><pre>version: &quot;3&quot;<br>services:<br>  app:<br>    build:<br>      context: .<br>      target: development<br>    env_file: .env<br>    volumes:<br>      - ./src:/usr/src/app/src<br>    ports:<br>      - 8081:80<br>    depends_on:<br>      - db<br>  db:<br>    image: postgres:14<br>    restart: always<br>    environment:<br>      POSTGRES_USER: admin<br>      POSTGRES_PASSWORD: admin<br>      POSTGRES_DB: my-startup-db<br>    volumes:<br>      - postgres-data:/var/lib/postgresql/data<br>    ports:<br>      - 5432:5432<br>volumes:<br>  postgres-data:</pre><p>Now we would go ahead and install some essential packages 📦</p><pre>npm install bcrypt cookie-parser cors jsonwebtoken pg-hstore stripe</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*iJfUAbaX2RXZBIhb.gif" /><figcaption>Installing Packages</figcaption></figure><p>Now, we would need to get our Stripe API keys 🔑. For this we would need to create a new account on Stripe.</p><p>Here we would be using Test Mode for demo.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*i3lp0364nls04Bb-.gif" /><figcaption>Stripe Keys</figcaption></figure><p>Here are the list of environment variables we would need for this project.</p><p>.env.example</p><pre>STRIPE_PUBLISHABLE_KEY=<br>STRIPE_SECRET_KEY=<br>POSTGRES_DB_URI=<br>secretKey=<br>CLIENT_URL=</pre><h3>Step 2: Creating Database Models</h3><p>Let’s begin by setting up our database now. 🐘</p><p>Since we’re utilizing the Sequelize ORM, we’ll need to create a model for our user data.</p><p>Here’s the code for our model 👇</p><p>models/userModel.js</p><pre>module.exports = (sequelize, DataTypes) =&gt; {<br>  const User = sequelize.define(<br>    &quot;user&quot;,<br>    {<br>      email: {<br>        type: DataTypes.STRING,<br>        unique: true,<br>        isEmail: true, //checks for email format<br>        allowNull: false,<br>      },<br>      password: {<br>        type: DataTypes.STRING,<br>        allowNull: false,<br>      },<br>      tier: {<br>        type: DataTypes.STRING,<br>        allowNull: true,<br>      },<br>    },<br>    { timestamps: true }<br>  );<br>  return User;<br>};</pre><h3>Step 2: Setting up the Routes</h3><p>Now, let’s go ahead and create our routes</p><p>POST /login - Helps to log in user and store the session</p><p>POST /signup - Helps create a new account</p><p>POST /create-checkout-session - Generates and Returns the stripe checkout page link</p><p>These 3 routes are separated into 2 files as follows:</p><p>routes/userRoutes.js</p><pre>const express = require(&quot;express&quot;);<br>const userController = require(&quot;../controllers/userController&quot;);<br><br>const { signup, login } = userController;<br>const userAuth = require(&quot;../middleware/userAuth&quot;);<br>const router = express.Router();<br>router.post(&quot;/signup&quot;, userAuth.saveUser, signup);<br>router.post(&quot;/login&quot;, login);<br>module.exports = router;</pre><p>routes/stripeRoute.js</p><pre>const express = require(&quot;express&quot;);<br>const { updatePlan } = require(&quot;../controllers/stripeController&quot;);<br><br>const router = express.Router();<br>router.post(&quot;/create-checkout-session&quot;, updatePlan);<br>module.exports = router;</pre><h3>Step 3: Setting Up User Profile</h3><p>🧑‍💻 For setting up the user profile, first we will define a middleware to check if the email address of a new user already exists in the database during signup.</p><p>middleware/userAuth.js</p><pre>//importing modules<br>const express = require(&quot;express&quot;);<br>const db = require(&quot;../models&quot;);</pre><p>Then we would go ahead and define our login and signup functions 👇</p><p>controllers/userController.js</p><pre>const bcrypt = require(&quot;bcrypt&quot;);<br>const db = require(&quot;../models&quot;);<br>const jwt = require(&quot;jsonwebtoken&quot;);<br><br>const User = db.users;<br>const signup = async (req, res) =&gt; {<br>  try {<br>    const { email, password } = req.body;<br>    console.log(email);<br>    const data = {<br>      email,<br>      password: await bcrypt.hash(password, 10),<br>    };<br>    //saving the user<br>    const user = await User.create(data);<br>    if (user) {<br>      let token = jwt.sign({ id: user.id }, process.env.secretKey, {<br>        expiresIn: 1 * 24 * 60 * 60 * 1000,<br>      });<br>      res.cookie(&quot;jwt&quot;, token, { maxAge: 1 * 24 * 60 * 60, httpOnly: true });<br>      console.log(&quot;user&quot;, JSON.stringify(user, null, 2));<br>      console.log(token);<br>      return res.status(201).send(user);<br>    } else {<br>      return res.status(409).send(&quot;Details are not correct&quot;);<br>    }<br>  } catch (error) {<br>    console.log(error);<br>  }<br>};<br><br>// Login Authentication<br>const login = async (req, res) =&gt; {<br>  try {<br>    const { email, password } = req.body;<br>    const user = await User.findOne({<br>      where: {<br>        email: email,<br>      },<br>    });<br>    if (user) {<br>      const isSame = await bcrypt.compare(password, user.password);<br>      if (isSame) {<br>        let token = jwt.sign({ id: user.id }, process.env.secretKey, {<br>          expiresIn: 1 * 24 * 60 * 60 * 1000,<br>        });<br>        res.cookie(&quot;jwt&quot;, token, { maxAge: 1 * 24 * 60 * 60, httpOnly: true });<br>        //send user data<br>        return res.status(201).send(user);<br>      } else {<br>        return res.status(401).send(&quot;Authentication failed&quot;);<br>      }<br>    } else {<br>      return res.status(401).send(&quot;Authentication failed&quot;);<br>    }<br>  } catch (error) {<br>    console.log(error);<br>  }<br>};<br>module.exports = {<br>  signup,<br>  login,<br>};</pre><h3>Step 4: Setting Up Stripe Checkout</h3><p>This is where we will integrate Stripe Checkout into our application.</p><p>We will use the Stripe API to manage payments and handle user subscriptions.</p><p>The following code creates a new Stripe checkout session. 💳</p><p>We will provide it with the payment method type, the product data, and the quantity.</p><p>We also need to specify the URLs where the user will be redirected upon a successful payment or if they cancel the transaction.</p><p>And, the server will respond back with the URL for the Stripe Session if everything is fine. ✅</p><p>controllers/stripeController.js</p><pre>const db = require(&quot;../models&quot;);<br>const Stripe = require(&quot;stripe&quot;);<br><br>const User = db.users;<br>require(&quot;dotenv&quot;).config();<br>const stripe = Stripe(process.env.STRIPE_SECRET_KEY);<br>const updatePlan = async (req, res) =&gt; {<br>  try {<br>    const { email, product } = req.body;<br>    const session = await stripe.checkout.sessions.create({<br>      payment_method_types: [&quot;card&quot;],<br>      line_items: [<br>        {<br>          price_data: {<br>            currency: &quot;usd&quot;,<br>            product_data: {<br>              name: product.name,<br>            },<br>            unit_amount: product.price * 100,<br>          },<br>          quantity: product.quantity,<br>        },<br>      ],<br>      mode: &quot;payment&quot;,<br>      success_url: `${process.env.CLIENT_URL}/success`,<br>      cancel_url: `${process.env.CLIENT_URL}/`,<br>    });<br>    //find a user by their email<br>    const user = await User.findOne({<br>      where: {<br>        email: email,<br>      },<br>    });<br>    if (user) {<br>      await user.update({ tier: product.name });<br>      return res.send({ url: session.url });<br>    } else {<br>      return res.status(401).send(&quot;User not found&quot;);<br>    }<br>  } catch (error) {<br>    console.log(error);<br>  }<br>};<br>module.exports = {<br>  updatePlan,<br>};</pre><p>At last, we would need to add all our routes to our entry point, which is server.js</p><p>server.js</p><pre>const cors = require(&quot;cors&quot;);<br>const express = require(&quot;express&quot;);<br>require(&quot;dotenv&quot;).config();<br>const cookieParser = require(&quot;cookie-parser&quot;);<br>const db = require(&quot;./models&quot;);<br>const userRoutes = require(&quot;./routes/userRoutes&quot;);<br>const PORT = process.env.PORT || 8080;<br>const stripeRoute = require(&quot;./routes/stripeRoute&quot;);<br>const app = express();<br><br>// Middlewares<br>app.use(express.json());<br>app.use(express.urlencoded({ extended: true }));<br>app.use(cookieParser());<br>app.use(cors());<br><br>// Routes<br>app.use(&quot;/api/v1/users&quot;, userRoutes);<br>app.use(&quot;/api/v1/stripe&quot;, stripeRoute);<br>app.listen(PORT, () =&gt; {<br>  console.log(&quot;Server started at port 8080&quot;);<br>  try {<br>    db.sequelize.sync({ force: true }).then(() =&gt; {<br>      console.log(&quot;db has been re sync&quot;);<br>    });<br>  } catch (error) {}<br>});</pre><p>And we are done with the backend ✅</p><p>Now let’s go ahead and try to deploy it on FL0. 🔼</p><h3>Step 5: Deploying with FL0</h3><p>🚀 For deploying our project to FL0 we will start with <a href="https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/How-to-push-an-existing-project-to-GitHub">pushing our repo</a> to a new GitHub repository first.</p><p>This is the link to our repository for reference: <a href="https://github.com/dalefl0/stripe-fl0-backend">https://github.com/dalefl0/stripe-fl0-backend</a></p><p>Now we would head on to <a href="https://app.fl0.dev/">app.fl0.dev</a> to start deploying.</p><ul><li>Here we would need to create a new project, let’s name it stripe-fl0 for example.</li><li>Now we would need to create a new Postgres instance. With Fl0, this takes less than 30 seconds! ⏳</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rh4ntTd9tSW36d_f.gif" /></figure><ul><li>After we have our database all set up, we would need to go ahead and deploy our backend in the same project.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*dXMzKK6e3lbubnFg.gif" /></figure><ul><li>After the backend is deployed we would need to import our database connection string as shown above ☝️</li></ul><p>🙌 Now we have our backend up and running.</p><p>Time for the UI ✨</p><h3>Step 6: Setting up the Frontend</h3><p>For setting up the frontend we would get started with the <a href="https://github.com/fl0zone/template-react-vite">template-react-vite</a>. ⚡️</p><p>This includes everything we need to get our React-Vite project up and running.</p><p>Now we would go ahead and install a few packages.</p><pre>npm install @heroicons/react axios react-router-dom<br>npm install postcss tailwindcss autoprefixer --save-dev</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*9N0E7-oVzJFWDyYb.gif" /></figure><h3>Step 7: Setting up the Frontend</h3><p>To build our UI components quickly we would take help of the <a href="https://tailwindui.com/components/marketing/sections/pricing">Pricing Section Component</a> and <a href="https://tailwindui.com/components/application-ui/forms/sign-in-forms">Sign-in and Registration Component</a> from <a href="https://tailwindui.com/components/">tailwind UI</a>.</p><p>For the sake of brevity, we will only look at the important functions of the frontend.</p><p>The complete project could be found at: <a href="https://github.com/dalefl0/stripe-fl0-frontend">https://github.com/dalefl0/stripe-fl0-frontend</a></p><p>Now, we would need to add a function to handle stripe checkouts</p><p>src/components/PricingPlans.jsx</p><pre>...<br><br>const handleCheckout = (product) =&gt; {<br>    axios<br>      .post(<br>        `https://stripe-fl0-backend-dev.fl0.io/api/v1/stripe/create-checkout-session`,<br>        {<br>          email,<br>          product,<br>        }<br>      )<br>      .then((res) =&gt; {<br>        if (res.data.url) {<br>          setTier(product.name);<br>          localStorage.setItem(&quot;tier&quot;, product.name);<br>          window.location.href = res.data.url;<br>        }<br>      })<br>      .catch((err) =&gt; navigate(&quot;/cancel&quot;));<br>  };<br>  <br>...</pre><p>This function calls our backend’s /create-checkout-session route, receives a link, and redirects the user to the checkout page. 📄</p><p>Apart from this, we need to also connect our signup and login pages to respective routes and store the user data in localstorage.</p><h3>Step 8: Deploying the Frontend</h3><p>For frontend we would need to again create a new <a href="https://github.com/dalefl0/stripe-fl0-frontend">repository</a> and deploy it in the same project in a similar manner.</p><p>We would then need to add the VITE_APP_API_BASE_URL environment variable to the frontend deployment which should be set to the URL of our backend.</p><p>We would also need to set the CLIENT_URL environment variable in the backend to the hosted URL of the frontend.</p><p>Once done, our FL0 project would look like this 👇</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*1eND-4Hkv1pZnjAY.png" /></figure><p>Now, let’s go ahead and try our application using this live demo link: <a href="https://stripe-fl0-frontend-q8oo-dev.fl0.io/">https://stripe-fl0-frontend-q8oo-dev.fl0.io/</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*uShfgrRVzKHHmyL-.gif" /><figcaption>Live Demo</figcaption></figure><h3>Wrapping up</h3><p>Thanks for sticking by till the end!</p><p>In this tutorial, we learnt how to build payment pages by integrating Stripe Checkout easily into our full-stack applications. 🎉</p><p>We also did blazingly-fast deployments of our project using <a href="https://fl0.com/">FL0</a>.</p><p>To get started with building your own applications with payment capabilities, head on to <a href="https://fl0.com/">fl0.com</a> 🚀</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*yKjjmuRljeHk3I8Drw9Hkg.gif" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c3558a4e8033" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fl0-engineering/building-seamless-payment-interfaces-with-stripe-and-fl0-c3558a4e8033">Building Seamless Payment Interfaces with Stripe and FL0</a> was originally published in <a href="https://medium.com/fl0-engineering">FL0 Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Slack ChatBot with GPT API, NodeJs, and FL0]]></title>
            <link>https://medium.com/fl0-engineering/building-a-slack-chatbot-with-gpt-api-nodejs-and-fl0-508c61593e2f?source=rss----ae7966bd51e3---4</link>
            <guid isPermaLink="false">https://medium.com/p/508c61593e2f</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[dockerfiles]]></category>
            <category><![CDATA[hosting]]></category>
            <category><![CDATA[artiificial-intelligence]]></category>
            <category><![CDATA[slackbot]]></category>
            <dc:creator><![CDATA[Dale Brett]]></dc:creator>
            <pubDate>Wed, 05 Jul 2023 21:58:13 GMT</pubDate>
            <atom:updated>2023-07-10T04:43:07.547Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*32Qg-SEkSPj-Cu2ZuEu0ug.png" /></figure><h3>TLDR;</h3><p>By the end of this guide, we will have a fully functioning Slack bot that can answer our questions about FL0 and its features using AI 🤖✨.</p><h3>Introduction</h3><p>The advent of <a href="https://openai.com/blog/openai-api">OpenAI</a>’s API has empowered countless developers to create sophisticated chatbots without breaking a sweat 🧑‍💻.</p><p>We’ve noticed that there’s a considerable amount of curiosity within the developer community regarding the workings and features of FL0. This gave us the idea to build a simple chatbot using the GPT API.</p><p>In this article, we would be building a slack chatbot named FL0Bot which could answer questions regarding FL0. 💬</p><p>We would be using NodeJs for our backend and Postgres as database. Then we would be deploying our application effortlessly with the help of FL0 🚀.</p><p>As we prepare to embark on this journey, let’s kick things off with a little humor. Here’s an xkcd comic strip to lighten the mood 👇</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/740/0*w6ZWN59JvmQ1R9pX.png" /><figcaption>Comic Strip</figcaption></figure><h3>Getting Started</h3><p>Let’s start with building our chatbot 💬.</p><p>To speed up things, in this tutorial we would be using the “<a href="https://github.com/fl0zone/blog-express-pg-sequelize">fl0zone/blog-express-pg-sequelize</a>” template.</p><p>You may refer to this blog for more details regarding the tutorial 👇</p><p><a href="https://blog.fl0.com/building-a-software-startup-on-fl0-part-1-f0624174e738">https://blog.fl0.com/building-a-software-startup-on-fl0-part-1-f0624174e738</a></p><p>In this template we have our basic NodeJs application and postgres database dockerized.</p><p>Here’s our docker-compose.yaml file for the same 🐳</p><pre>version: &quot;3&quot;<br>services:<br>  app:<br>    build:<br>      context: .<br>      target: development<br>    env_file: .env<br>    volumes:<br>      - ./src:/usr/src/app/src<br>    ports:<br>      - 8081:80<br>    depends_on:<br>      - db<br>  db:<br>    image: postgres:14<br>    restart: always<br>    environment:<br>      POSTGRES_USER: admin<br>      POSTGRES_PASSWORD: admin<br>      POSTGRES_DB: my-startup-db<br>    volumes:<br>      - postgres-data:/var/lib/postgresql/data<br>    ports:<br>      - 5432:5432<br>volumes:<br>  postgres-data:</pre><h3>Folder Structure</h3><p>Before we get started, here’s a look at our final project folder structure for reference 📂</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/748/0*26M_9YadAw3Z53fZ.png" /><figcaption>Folder Structure</figcaption></figure><p>And here’s a high level overview of what we are gonna build 👀</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-Jh_fDyGXT5Zk-3T.png" /></figure><p>Now, let’s delve into the code 🧑‍💻</p><h3>Step 1: Project Setup</h3><p>After we have created our new project using the above template, we would first need to install a few packages.</p><pre>npm install axios @slack/bolt openai uuid</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*aytJHYFfJd-On64O.gif" /><figcaption>Installing Packages</figcaption></figure><p>Now, we would need to get our OpenAI API key 🔑.</p><p>For this, we would need to create our account at <a href="https://platform.openai.com/">platform.openai.com</a>.</p><p>After this, we would select the “API” option, and click on “View API Keys” in account options.</p><p>Now, we would need to go ahead and create a new API key as shown below 👇</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*kWjivoPL6ywUojxg.gif" /><figcaption>OpenAPI setup</figcaption></figure><h3>Step 2: Config Setup</h3><p>We would create a .env.example file to list the environment variables just for reference 👇</p><pre>NODE_ENV=development<br>DATABASE_URL=postgres_url<br>BOT_SYSTEM=system_prompt<br>OPENAI_API_KEY=open_api_key<br>SLACK_WEBHOOK=slack_webhook</pre><p>Then, we would need to go ahead and add these variables to our already present config file 📝</p><p>src/config/index.js</p><pre>module.exports = {<br>  &quot;local&quot;: {<br>    &quot;use_env_variable&quot;: &quot;DATABASE_URL&quot;,<br>    &quot;openai_api_key&quot;: &quot;OPENAI_API_KEY&quot;,<br>    &quot;bot_system&quot; : &quot;BOT_SYSTEM&quot;,<br>    &quot;slack_webhook&quot; : &quot;SLACK_WEBHOOK&quot;,<br>    synchronize: true<br>  },<br>  &quot;development&quot;: {<br>    &quot;use_env_variable&quot;: &quot;DATABASE_URL&quot;,<br>    &quot;openai_api_key&quot;: &quot;OPENAI_API_KEY&quot;,<br>    &quot;bot_system&quot; : &quot;BOT_SYSTEM&quot;,<br>    &quot;slack_webhook&quot; : &quot;SLACK_WEBHOOK&quot;,<br>    synchronize: true<br>  },<br>  &quot;production&quot;: {<br>    &quot;use_env_variable&quot;: &quot;DATABASE_URL&quot;,<br>    &quot;openai_api_key&quot;: &quot;OPENAI_API_KEY&quot;,<br>    &quot;bot_system&quot; : &quot;BOT_SYSTEM&quot;,<br>    &quot;slack_webhook&quot; : &quot;SLACK_WEBHOOK&quot;,<br>    synchronize: true<br>  }<br>}</pre><h3>Step 3: Creating Models</h3><p>Now let’s get started with setting up our database. As we are using sequelize ORM, we would need to create models for our postgres database 🐘.</p><p>Here we would need to create a Chat in which we would be storing all the communication between the Fl0Bot and User.</p><p>Everytime a new request is made, we SELECT the recent chats from this database and send it for reference to the Fl0Bot. 💬</p><p>src/models/chat.js</p><pre>&#39;use strict&#39;;<br>const { Sequelize, DataTypes } = require(&#39;sequelize&#39;);<br><br>module.exports = (sequelize) =&gt; {<br>  const Chat = sequelize.define(<br>    &#39;Chat&#39;,<br>    {<br>      chat_id: {<br>        type: DataTypes.UUID,<br>        primaryKey: true,<br>        defaultValue: Sequelize.UUIDV4,<br>      },<br>      person_id: {<br>        type: DataTypes.STRING,<br>        allowNull: false,<br>      },<br>      role: {<br>        type: DataTypes.STRING,<br>      },<br>      content: {<br>        type: DataTypes.STRING(10000)<br>      },<br>      time_created: {<br>        type: DataTypes.DATE,<br>        defaultValue: DataTypes.NOW,<br>      },<br>      time_updated: {<br>        type: DataTypes.DATE,<br>        defaultValue: DataTypes.NOW,<br>      },<br>    },<br>    {<br>      tableName: &#39;chats&#39;, // Specify the table name explicitly if different from the model name<br>      timestamps: false, // Disable timestamps (createdAt, updatedAt)<br>      hooks: {<br>        beforeValidate: (chat, options) =&gt; {<br>          // Update the time_updated field to the current timestamp before saving the record<br>          chat.time_updated = new Date();<br>        },<br>      },<br>    }<br>  );<br>  return Chat;<br>};</pre><h3>Step 4: Creating the Chat Bot</h3><p>Now let’s move on to writing the code for our ChatBot! 🤖</p><p>First we would create our handleAppMention function.</p><p>Here we’re parsing the text message, excluding any mentions, then looking for an existing user chat session or creating one if it doesn’t exist.</p><p>We’re fetching the last five chat messages to maintain the context of the conversation. 💬✨</p><p>Here we’re leveraging OpenAI’s API to get a completion response to the user’s input. 🤖</p><p>We are also adding a system in the conversation which is in the config.bot_system. This provides GPT the context about Fl0.</p><p>Example GPT System Prompt</p><pre>You are a bot that answers queries only around a specific product: fl0 and you will tell nothing about any other product or tools. FL0 is a platform for easily deploying your code as containers. Just push code to your repo and FL0 will build and deploy your app to a fully managed infrastructure complete with databases, logging, multiple environments and lots more!</pre><p>src/index.js</p><pre>async function handleAppMention({event}) {<br>const mentionRegex = /&lt;@[\w\d]+&gt;/g; // Regex pattern to match the mention<br>  const msg = event.text.replace(mentionRegex, &#39;&#39;);<br>  const person_id = event.user;<br>  const query = msg;<br>  try {<br>    const userExists = await Chat.findOne({ where: { person_id: person_id }, raw: true });<br>    if (!userExists) {<br>      const dbChat = await Chat.create({ person_id: person_id, role: &#39;system&#39;, content: process.env[config.bot_system] });<br>    }<br>    const chats = await Chat.findAll({ where: { person_id }, order: [[&#39;time_created&#39;, &#39;DESC&#39;]], limit: 5, raw: true });<br>    const chatsGpt = chats.map((item) =&gt; ({ role: item.role, content: item.content }));<br>    chatsGpt.push({ role: &#39;user&#39;, content: query });<br>    const response = await openai.createChatCompletion({<br>      model: &#39;gpt-3.5-turbo&#39;,<br>      messages: chatsGpt,<br>    });<br>    await Chat.bulkCreate([<br>      { person_id, role: &#39;user&#39;, content: query },<br>      { person_id, role: &#39;assistant&#39;, content: response.data.choices[0].message.content }<br>    ]);<br>    await axios.post(process.env[config.slack_webhook], {text: response.data.choices[0].message.content});<br>    return response.data.choices[0].message.content<br>  } catch (error) {<br>    console.log(&quot;ERROR&quot;,error)<br>    return &#39;Failed to process chat&#39;;<br>  }<br>}</pre><p>🚗 Coming to our routes, we’ve set up an endpoint (/slack/action-endpoint) for Slack&#39;s action-events, in response to app_mention events.</p><p>And we are returning the response from handleAppMention function.</p><p>This response would be sent back by our Slack Bot.</p><p>src/index.js</p><pre>const express = require(&#39;express&#39;)<br>const { sequelize, Chat } = require(&#39;./models&#39;);<br><br>const process = require(&#39;process&#39;);<br>const env = process.env.NODE_ENV || &#39;development&#39;;<br>const config = require(__dirname + &#39;/config/index.js&#39;)[env];<br>const axios = require(&#39;axios&#39;);<br><br>const app = express()<br>app.use(express.json());<br><br>const { Configuration, OpenAIApi } = require(&quot;openai&quot;);<br>const configuration = new Configuration({<br>  apiKey: process.env[config.openai_api_key],<br>});<br>const openai = new OpenAIApi(configuration);<br>const port = process.env.PORT ?? 3000;<br>app.post(&#39;/slack/action-endpoint&#39;, async (req, res) =&gt; {<br>  const { challenge } = req.body;<br>  if (challenge) {<br>    res.status(200).send(challenge);<br>  } else {<br>      try {<br>        switch(req.body.event.type) {<br>          case &#39;app_mention&#39;:<br>            const response = handleAppMention(req.body)<br>            res.status(200).json({ message: &#39;Success&#39; });<br>            break<br>          default:<br>            res.status(400).json({ message: &#39;Bad Request&#39; });<br>            break<br>        }<br>      } catch (error) {<br>        console.error(`Error processing Slack event: ${error}`);<br>        res.status(500).json({ message: error });<br>      }<br>  }<br>});<br>app.listen(port, async () =&gt; {<br>  console.log(`Example app listening on port ${port}`)<br>  try {<br>    await sequelize.sync({ force: false });<br>    await sequelize.authenticate();<br>    sequelize.options.dialectOptions.ssl = false;<br>    await sequelize.sync({ force: true});<br>    console.log(&#39;Connection has been established successfully.&#39;);<br>  } catch (error) {<br>    console.error(&#39;Unable to connect to the database:&#39;, error);<br>  }<br>});</pre><h3>Step 5: Deploying with Fl0</h3><p>Now that we have a functional API and database, its time to deploy them to a server! 🚀</p><p>In this tutorial, we’re utilizing FL0, a platform expertly designed for straightforward deployment of dockerized NodeJS applications, fully integrated with a database.</p><p>We would just need to push our repo to GitHub.🫸</p><p>Now we would be deploying our project just by “Connecting our GitHub account&quot; and selecting our project.</p><p>Then we would be adding our environment variables listed in .env.example file.</p><p>You may find a detailed process of deployment in this blog 👉 <a href="https://blog.fl0.com/building-a-software-startup-on-fl0-part-1-f0624174e738">https://blog.fl0.com/building-a-software-startup-on-fl0-part-1-f0624174e738</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*0DlwXYdZTrBC9t8q.png" /><figcaption>FL0 Deployment</figcaption></figure><h3>Step 6: Setting up Slack App</h3><p>Now that our project is set up, let’s create our Slack App.</p><p>We would visit <a href="https://api.slack.com/apps">https://api.slack.com/apps</a> and click on Create New App.</p><p>We would name our bot “FL0Bot” 😁</p><p>In the Event Subscriptions section, we would enable events, set the request URL, and subscribe to bot events: app_mention</p><p>We would also need to get our webhook and pass it as an environment variable to our FL0 hosting.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*Q5xs_QA4g-QNCaIV.gif" /></figure><h3>Conclusion</h3><p>So, there we have it — a completely operational chatbot tailored to answer questions about FL0 and its features, built using NodeJs, Postgres, and OpenAI&#39;s GPT, and seamlessly deployed with FL0!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/816/0*UpfbzcnOkH-acZFA.png" /></figure><p>Here’s the link to our repository for reference ➡️ <a href="https://github.com/sanskarseth/fl0bot">Visit FL0Bot Repo</a></p><p>The power of OpenAI’s APIs and quick deployments with FL0, make it effortless to build our own AI bots 🚀🎉.</p><p>Head on to <a href="https://www.fl0.com/">fl0.com</a> to start building your own bots 🧑‍💻.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*yKjjmuRljeHk3I8Drw9Hkg.gif" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=508c61593e2f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fl0-engineering/building-a-slack-chatbot-with-gpt-api-nodejs-and-fl0-508c61593e2f">Building a Slack ChatBot with GPT API, NodeJs, and FL0</a> was originally published in <a href="https://medium.com/fl0-engineering">FL0 Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The best way to debug a Node.js app running in a Docker container]]></title>
            <link>https://medium.com/fl0-engineering/the-best-way-to-debug-a-node-js-app-running-in-a-docker-container-99241afb4781?source=rss----ae7966bd51e3---4</link>
            <guid isPermaLink="false">https://medium.com/p/99241afb4781</guid>
            <category><![CDATA[docker-compose]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[debugging]]></category>
            <category><![CDATA[visual-studio-code]]></category>
            <dc:creator><![CDATA[James Harrison]]></dc:creator>
            <pubDate>Wed, 15 Feb 2023 23:51:43 GMT</pubDate>
            <atom:updated>2023-02-16T00:35:55.019Z</atom:updated>
            <content:encoded><![CDATA[<p>In my previous article <a href="https://medium.com/fl0-engineering/the-perfect-multi-stage-dockerfile-for-node-js-apps-981dd61bdb34">The perfect multi-stage Dockerfile for Node.js apps</a> I explained how to set up a Dockerfile that would work in both a local environment with hot-reloading and in production as a minified image.</p><p>This article extends from where we left off, but adds a critical element: debugging!</p><p>Before continuing, please have a read of the <a href="https://medium.com/fl0-engineering/the-perfect-multi-stage-dockerfile-for-node-js-apps-981dd61bdb34">previous article</a> or clone the <a href="https://github.com/fl0zone/blog-express-pg-sequelize/tree/multi-stage-dockerfile">example repository</a> (be sure to check out the branch called <strong>multi-stage-dockerfile</strong>)!</p><p>We’re going to cover the following:</p><ol><li>Updating package.json with a debug script</li><li>Creating a debug version of our docker-compose.yml file</li><li>Attaching a debugger to the container</li><li>Automating Visual Studio Code to start and stop containers</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/848/0*OwhDGdZROiW0J2pn" /><figcaption>The Docker whale being inspected by engineers in a warehouse (thanks, AI)</figcaption></figure><h4>Deploying to the cloud</h4><p>If you want a really simple way to deploy your container to a scalable infrastructure, check out <a href="https://fl0.com">FL0</a>! It’s a platform that makes it as simple as possible to go from code to cloud, complete with dev/prod environments, databases and more. All you have to do is commit your code and <a href="https://fl0.com">FL0</a> will handle the rest.</p><p><a href="https://www.fl0.com">FL0 - backend engineering, supercharged</a></p><h3>Creating the debug script</h3><p>Open up package.json and take a look at the scripts section. There’s currently a start and a start:dev option. Our Dev script uses <a href="https://www.npmjs.com/package/nodemon">Nodemon</a> to watch for changes and reload the server as needed. Nodemon also accepts a flag called --inspectto run in debug mode. If you’re interested in some thrilling reading, learn more in the <a href="https://nodejs.org/en/docs/guides/debugging-getting-started/#command-line-options">Node.js docs</a>!</p><p>Open up your package.json file and add a new script called start:debug.</p><pre>{<br>  ...<br>  &quot;scripts&quot;: {<br>    ...<br>    &quot;start:debug&quot;: &quot;nodemon -r dotenv/config --inspect=0.0.0.0 src/index.js&quot;,<br>  },<br>  ...<br>}</pre><p>It’s the same as our start:dev script, but we pass the --inspect flag and the IP address 0.0.0.0, meaning we are allowing debugger connections from any IP address.</p><blockquote><strong>Note:</strong> If you try and run this script in your terminal, you’ll get an error that the database can’t be found. It needs to be run with Docker Compose so that the database is also provisioned.</blockquote><h3>Creating a debug Docker Compose file</h3><p>Docker Compose has a great feature called <a href="https://docs.docker.com/compose/extends/">overrides</a> which allows you to have multiple docker-compose.yml files in your codebase, one overriding parts of the other. This means you can have a base docker-compose.yml and a docker-compose.debug.yml file that overrides things like the port and the start command. Let’s go and set that up!</p><p>Create a new file in your repo called docker-compose.debug.yml and paste in the following:</p><pre>version: &#39;3.4&#39;<br><br>services:<br>  app:<br>    ports: <br>     - 3000:80<br>     - 9229:9229<br>    command: [&quot;npm&quot;, &quot;run&quot;, &quot;start:debug&quot;]</pre><p>You can see it’s pretty minimal. All it does is override the ports and command section of our main file. And the command we’re running is our newly created start:debug command! The port 9229 is the default port for debugging with Node.js, and we map that from the container to our host machine so that our IDE can connect properly.</p><p>If we ran docker compose up right now it would only use our original YAML file. But if we run the following, it will use both files:</p><pre>$ docker compose -f docker-compose.yml -f docker-compose.debug.yml up</pre><p>Go ahead and try that out! In the terminal output you should see a couple of important lines that indicate Node was started in debug mode successfully:</p><pre>[nodemon] starting `node -r dotenv/config --inspect=0.0.0.0 src/index.js`<br>Debugger listening on ws://0.0.0.0:9229/51b990e4-17fd-40ef-9a4c-784f033329d2</pre><p>If you see that, we’re kicking goals! If not, maybe scroll through Instagram for a while and see if it fixes itself. If you’re <em>really</em> stuck, leave a comment below and I’ll get back to you!</p><h3>Attaching a debugger to the container</h3><p>While there are lots of available debuggers, we’re going to focus on Visual Studio Code (VSC) in this article. With your containers up and running in Debug mode, create a folder and file in the root of your repo called .vscode/launch.jsonand add this content:</p><pre>{<br>    // Use IntelliSense to learn about possible attributes.<br>    // Hover to view descriptions of existing attributes.<br>    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387<br>    &quot;version&quot;: &quot;0.2.0&quot;,<br>    &quot;configurations&quot;: [<br>        <br>        <br>    ]<br>}</pre><p>With this file open, click cmd+shift+p (ctrl+shift+p) to open the command palette and select an option called Debug: Add Configuration.... From the list, select Node.js: Attach to Remote Program.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/679/1*O2FjM-pPYklgjTVj27ZALQ.png" /><figcaption>Using the command palette to add a launch configuration</figcaption></figure><p>You should see some JSON added to your launch.json file. Modify the file as follows:</p><ol><li>Set the address property to “localhost” as we have mapped port 9229 from our container to our host machine</li><li>Set the remoteRoot property to /usr/src/app as this is our Dockerfile’s WORKDIR and contains all our source code (inside the container)</li><li>Set a new property called restart so that when we make a code change and nodemon restarts the server, we don’t lose our debugger connection</li></ol><p>Your file should look like this:</p><pre>{<br>    // Use IntelliSense to learn about possible attributes.<br>    // Hover to view descriptions of existing attributes.<br>    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387<br>    &quot;version&quot;: &quot;0.2.0&quot;,<br>    &quot;configurations&quot;: [<br>        {<br>            &quot;address&quot;: &quot;localhost&quot;,<br>            &quot;localRoot&quot;: &quot;${workspaceFolder}&quot;,<br>            &quot;name&quot;: &quot;Attach to Remote&quot;,<br>            &quot;port&quot;: 9229,<br>            &quot;remoteRoot&quot;: &quot;/usr/src/app&quot;,<br>            &quot;request&quot;: &quot;attach&quot;,<br>            &quot;skipFiles&quot;: [<br>                &quot;&lt;node_internals&gt;/**&quot;<br>            ],<br>            &quot;type&quot;: &quot;node&quot;,<br>            &quot;restart&quot;: true<br>        }<br>    ]<br>}</pre><p>Once you save the file you’ll see an option in the Debug panel to launch your configuration. Go ahead and click it! Just be sure your containers are running in debug mode first.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/418/1*cz4NpM9yrbgtwGsyfTOUBQ.png" /><figcaption>Launching our new debug configuration</figcaption></figure><p>If all goes well you should see an orange bar at the bottom of VSC. Open index.js and set a couple of breakpoints in the request handlers:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/539/1*8ct2Nn85bpzg_j_GGe2K-w.png" /><figcaption>Setting breakpoints in index.js</figcaption></figure><p>In your browser, load up <a href="http://localhost:3000/">http://localhost:3000/</a>. If your debugger is working, it should pause at the breakpoints you just set. Congratulations, you just attached a debugger to a Docker container! But don’t get overly attached just yet, we still have more to do…</p><h3>Automating Visual Studio Code to start and stop containers</h3><p>What we’ve got so far is great, but it requires some manual steps. Starting our containers, running the debugger, disconnecting the debugger, stopping the containers. If you’re happy with this…that’s fine! It definitely gives you the most control. But if you’d like to automate these steps, read on…</p><p>Create a new file in your .vscode folder called tasks.json and update it to look like this:</p><pre>{<br>    // See https://go.microsoft.com/fwlink/?LinkId=733558<br>    // for the documentation about the tasks.json format<br>    &quot;version&quot;: &quot;2.0.0&quot;,<br>    &quot;tasks&quot;: [<br>        {<br>            &quot;type&quot;: &quot;docker-compose&quot;,<br>            &quot;label&quot;: &quot;docker-compose: debug&quot;,<br>            &quot;dockerCompose&quot;: {<br>                &quot;up&quot;: {<br>                    &quot;detached&quot;: true,<br>                    &quot;build&quot;: true<br>                },<br>                &quot;files&quot;: [<br>                    &quot;${workspaceFolder}/docker-compose.yml&quot;,<br>                    &quot;${workspaceFolder}/docker-compose.debug.yml&quot;<br>                ]<br>            }<br>        },<br>        {<br>            &quot;type&quot;: &quot;docker-compose&quot;,<br>            &quot;label&quot;: &quot;docker-compose: down&quot;,<br>            &quot;dockerCompose&quot;: {<br>                &quot;down&quot;: {}<br>            }<br>        }<br>    ]<br>}</pre><p>The first task called docker-compose: debug will start our containers using the docker-compose.debug.yml configuration, and the second will stop them again. Creating tasks like this means we can call them from our launch.json configuration. Open up your launch config and add a couple of new lines:</p><pre>{<br>    ...<br>    &quot;configurations&quot;: [<br>        {<br>            ...<br>            &quot;preLaunchTask&quot;: &quot;docker-compose: debug&quot;,<br>            &quot;postDebugTask&quot;: &quot;docker-compose: down&quot;<br>        }<br>    ]<br>}</pre><p>These new lines will run before and after the debugger starts, meaning our containers will start and stop automatically! Go ahead and try it out, but make sure your containers are stopped first. Check out the video below to see it in action.</p><h3>James Harrison on Twitter: &quot;Debugging a Node.js app running inside a Docker container complete with hot-reloading and auto start/stop! pic.twitter.com/g22mGOrhOo / Twitter&quot;</h3><p>Debugging a Node.js app running inside a Docker container complete with hot-reloading and auto start/stop! pic.twitter.com/g22mGOrhOo</p><p>Thanks for reading! I’d love to hear your questions, suggestions and feedback so please don’t hesitate to leave a comment.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=99241afb4781" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fl0-engineering/the-best-way-to-debug-a-node-js-app-running-in-a-docker-container-99241afb4781">The best way to debug a Node.js app running in a Docker container</a> was originally published in <a href="https://medium.com/fl0-engineering">FL0 Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The perfect multi-stage Dockerfile for Node.js apps]]></title>
            <link>https://medium.com/fl0-engineering/the-perfect-multi-stage-dockerfile-for-node-js-apps-981dd61bdb34?source=rss----ae7966bd51e3---4</link>
            <guid isPermaLink="false">https://medium.com/p/981dd61bdb34</guid>
            <category><![CDATA[postgres]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[expressjs]]></category>
            <dc:creator><![CDATA[James Harrison]]></dc:creator>
            <pubDate>Thu, 02 Feb 2023 22:24:34 GMT</pubDate>
            <atom:updated>2023-02-20T20:12:26.133Z</atom:updated>
            <content:encoded><![CDATA[<p>If you’re reading this, chances are you’ve been scouring the web for a decent article on setting up a good multi-stage Dockerfile for your Node.js project. One that will hot-reload when you make a code change. One that has a production version that’s as small as possible. Well, this is the last article you’ll have to read!</p><p>Here’s the list of things we wanted to make sure our Dockerfile could do:</p><ol><li>Run locally with dev dependencies</li><li>Hot-reload when code is changed on our machine</li><li>Build a production-ready version that excludes dev dependencies</li><li>Produce in a reasonably small container image file size</li><li><strong>Edit:</strong> Debugging with breakpoints!</li></ol><p>Seems simple enough, right? Well there are a lot of blog posts out there for each one of those dot points, but we aim to put them all together in a simple guide.</p><h3>Getting Started</h3><h4>Example Repository</h4><p>We’re going to use the <a href="https://github.com/fl0zone/blog-express-pg-sequelize/tree/building-deploying-30-mins">fl0zone/blog-express-pg-sequelize</a> from a <a href="https://medium.com/fl0-engineering/building-a-software-startup-on-fl0-part-1-f0624174e738">previous blog post</a> as our starting point. Check out that code, and make sure to use the branch called <strong>building-deploying-30-mins!</strong></p><p>And if you want to skip ahead to the answers, you can find the completed code from this article in the same repo on the <strong>multi-stage-dockerfile </strong>branch.</p><h4>Environment Variables</h4><p>Copy the .env.example file in the root of the repo and call it .env.</p><h4>Deploying to the cloud</h4><p>If you want a really simple way to deploy your container to a scalable infrastructure, check out <a href="https://fl0.com">FL0</a>! It’s a platform that makes it as simple as possible to go from code to cloud, complete with dev/prod environments, databases and more. All you have to do is commit your code and <a href="https://fl0.com">FL0</a> will handle the rest.</p><p><a href="https://www.fl0.com">FL0 - backend engineering, supercharged</a></p><h3>The Basics</h3><p>Our sample repository already has a basic docker-compose.yml file which is currently being used to spin up a local Postgres database. To run our app, use these commands:</p><pre>$ npm install<br>$ docker compose up -d<br>$ npm run start:dev</pre><p>Verify your app is working by visiting <a href="http://localhost:3000/">http://localhost:3000/</a> in your browser.</p><p>Now, instead of running our app on our local machine, we want to run it inside a Docker container. Let’s start by creating a file called Dockerfile in the root of the repository:</p><pre># Use a Node.js base image so we don&#39;t have to install a bunch of extra things<br>FROM node:16<br><br>WORKDIR /usr/src/app<br><br># Copy the package.json and package-lock.json files over<br># We do this FIRST so that we don&#39;t copy the huge node_modules folder over from our local machine<br># The node_modules can contain machine-specific libraries, so it should be created by the machine that&#39;s actually running the code<br>COPY package*.json ./<br><br># Now we run NPM install, which includes dev dependencies<br>RUN npm install<br><br># Copy the rest of our source code over to the image<br>COPY ./src ./src<br><br>EXPOSE 80<br><br># Run our start:dev command, which uses nodemon to watch for changes<br>CMD [ &quot;npm&quot;, &quot;run&quot;, &quot;start:dev&quot; ]</pre><p>The comments in the file explain each step. It’s a pretty simple Dockerfile!</p><p>Next, in order to use this Dockerfile we need to modify our docker-compose.yml and add a new service called app that uses the Dockerfile.</p><pre>version: &#39;3&#39;<br>services:<br>  app:<br>    build: .<br>    env_file: .env<br>    ports: <br>     - 3000:80<br>    depends_on:<br>     - db<br>  db:<br>    image: postgres:14<br>    restart: always<br>    environment:<br>      POSTGRES_USER: ${PGUSER}<br>      POSTGRES_PASSWORD: ${PGPASSWORD}<br>      POSTGRES_DB: ${PGDATABASE}<br>    volumes:<br>      - postgres-data:/var/lib/postgresql/data<br>    ports:<br>      - 5432:5432<br>volumes:<br>  postgres-data:</pre><p>As you can see, it specifies the current directory as the build context (which means it reads our new Dockerfile), passes in the .env file we already have, maps port 3000 of our local machine to port 80 of the Docker container and makes sure the db service is already running before starting.</p><p>Two last change to make, and they’re both to our .env file.</p><ol><li>The PORT variable needs to be set to 80 instead of 3000, because our Dockerfile exposes 80 and then gets mapped to 3000 on our local machine with the docker-compose.yml file.</li><li>The DATABASE_URL needs to be updated to point to the db service instead of localhost. This is because comms between our app and database is now happening between two containers, not from our machine into a Docker image.</li></ol><p>Make sure your .env file looks like this:</p><pre>NODE_ENV=local<br>PORT=80<br>PGUSER=admin<br>PGPASSWORD=admin<br>PGDATABASE=my-startup-db<br>DATABASE_URL=postgres://admin:admin@db:5432/my-startup-db</pre><p>Let’s test it out! Open up a terminal and run the following command:</p><pre>$ docker compose up</pre><p>Verify it’s working by reloading <a href="http://localhost:3000/">http://localhost:3000/</a> in your browser.</p><h3>Hot-Reloading</h3><p>This is great, but if you make a change to your code you will see that the Docker image isn’t being updated. This is because we copied the source code over, and there is one copy on our local machine and another in the Docker image.</p><p>To fix this, we can mount a volume on our Docker image so that our local machine and container both share the same code. Update your docker-compose.yml file to look like this, noticing the volumes section:</p><pre>version: &#39;3&#39;<br>services:<br>  app:<br>    build: .<br>    env_file: .env<br>    ports: <br>     - 3000:80<br>    volumes:<br>      - ./src:/usr/src/app/src<br>    depends_on:<br>     - db<br>  db:<br>    image: postgres:14<br>    restart: always<br>    environment:<br>      POSTGRES_USER: ${PGUSER}<br>      POSTGRES_PASSWORD: ${PGPASSWORD}<br>      POSTGRES_DB: ${PGDATABASE}<br>    volumes:<br>      - postgres-data:/var/lib/postgresql/data<br>    ports:<br>      - 5432:5432<br>volumes:<br>  postgres-data:</pre><p>Now, if we make a change to our source code, we can refresh the browser and see the changes updated instantly! Run this to make the changes take effect:</p><pre>$ docker compose up --build</pre><h3>Building for Production</h3><p>The solution we have so far is great for working locally, but there are a couple of problems if we want to use this for Production. The image size is way too big. Run docker container ls -s to see your app container is probably around 800mb). The other problem is that we ran npm install, which includes dev dependencies and things we may not want to ship.</p><p>One of the great things about Docker is its ability to have <a href="https://docs.docker.com/build/building/multi-stage/">multi-stage builds</a> inside a single Dockerfile. This means we can keep our Development build, and do some optimization for production. And what’s even better is we can copy files from one “stage” into another. Here’s what we’ll do:</p><ol><li>Keep our “Development” stage as-is</li><li>Create a new “Builder” stage that extends from the “Development” stage but removes the node_modules folder and runs an NPM clean install with production-only dependencies</li><li>Create a “Production” stage using a much smaller Docker image and copy the built files across from the “Builder” stage</li></ol><p>Open up your Dockerfile and alter it to look like this:</p><pre># Use a Node.js base image so we don&#39;t have to install a bunch of extra things<br>FROM node:16 as development<br><br>WORKDIR /usr/src/app<br><br># Copy the package.json and package-lock.json files over<br># We do this FIRST so that we don&#39;t copy the huge node_modules folder over from our local machine<br># The node_modules can contain machine-specific libraries, so it should be created by the machine that&#39;s actually running the code<br>COPY package*.json ./<br><br># Now we run NPM install, which includes dev dependencies<br>RUN npm install<br><br># Copy the rest of our source code over to the image<br>COPY ./src ./src<br><br>EXPOSE 80<br><br># Run our start:dev command, which uses nodemon to watch for changes<br>CMD [ &quot;npm&quot;, &quot;run&quot;, &quot;start:dev&quot; ]<br><br># &quot;Builder&quot; stage extends from the &quot;development&quot; stage but does an NPM clean install with only production dependencies <br>FROM development as builder<br>WORKDIR /usr/src/app<br>RUN rm -rf node_modules<br>RUN npm ci --only=production<br>EXPOSE 80<br>CMD [ &quot;npm&quot;, &quot;start&quot; ]<br> <br># Final stage uses a very small image and copies the built assets across from the &quot;builder&quot; stage<br>FROM alpine:latest as production<br>RUN apk --no-cache add nodejs ca-certificates<br>WORKDIR /root/<br>COPY --from=builder /usr/src/app ./<br>CMD [ &quot;node&quot;, &quot;src/index.js&quot; ]</pre><p>We now have three FROM statements, each extending on from the last. Each stage has a different CMD as well, so we can run with watch mode turned on in Development but use Node directly in Production.</p><p>If we were to build this Dockerfile now, it would execute all three stages. Which is great except when we want to run something in Development. To do that, we need to change our docker-compose.yml file to specify which target to build. Open it up and change it to look like this:</p><pre>version: &#39;3&#39;<br>services:<br>  app:<br>    build:<br>      context: .<br>      target: development<br>    env_file: .env<br>    ports: <br>     - 3000:80<br>    volumes:<br>      - ./src:/usr/src/app/src<br>    depends_on:<br>     - db<br>  db:<br>    image: postgres:14<br>    restart: always<br>    environment:<br>      POSTGRES_USER: ${PGUSER}<br>      POSTGRES_PASSWORD: ${PGPASSWORD}<br>      POSTGRES_DB: ${PGDATABASE}<br>    volumes:<br>      - postgres-data:/var/lib/postgresql/data<br>    ports:<br>      - 5432:5432<br>volumes:<br>  postgres-data:</pre><p>The build section which now specifies a context of the current directory, and a target of our Development stage.</p><p>Go ahead and try it out! Run docker compose up again and notice the hot-reloading works. If you want to try out your production build, you can run this:</p><pre>$ docker build . -t my-startup:latest</pre><p>And check this out…the image size of the Development stage is 884MB vs 73.7MB for Production! That’s going to be way faster to work with and deploy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/449/1*lAtDzg2zBKPFA-73Jeg0tg.png" /></figure><h3>Debugging with Breakpoints</h3><p>To take this a step further and debug the code inside your container with an IDE, see my follow-up article <a href="https://medium.com/fl0-engineering/the-best-way-to-debug-a-node-js-app-running-in-a-docker-container-99241afb4781">The best way to debug a Node.js app running in a Docker container</a>.</p><p><a href="https://medium.com/fl0-engineering/the-best-way-to-debug-a-node-js-app-running-in-a-docker-container-99241afb4781">The best way to debug a Node.js app running in a Docker container</a></p><h3>Deploying</h3><p>Speaking of deploying, how can we easily get this application up and running in a cloud environment in a scalable way, complete with database? Well if you’re using FL0, you can just commit your code and the platform will handle the rest! Check out the <a href="https://docs.fl0.com/fl0-docs/">FL0 docs</a> on how to get setup.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=981dd61bdb34" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fl0-engineering/the-perfect-multi-stage-dockerfile-for-node-js-apps-981dd61bdb34">The perfect multi-stage Dockerfile for Node.js apps</a> was originally published in <a href="https://medium.com/fl0-engineering">FL0 Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building and deploying a NodeJS + Postgres API in less than 30 minutes]]></title>
            <link>https://medium.com/fl0-engineering/building-a-software-startup-on-fl0-part-1-f0624174e738?source=rss----ae7966bd51e3---4</link>
            <guid isPermaLink="false">https://medium.com/p/f0624174e738</guid>
            <category><![CDATA[postgres]]></category>
            <category><![CDATA[expressjs]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[sequelize]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[James Harrison]]></dc:creator>
            <pubDate>Mon, 30 Jan 2023 21:18:03 GMT</pubDate>
            <atom:updated>2023-02-07T18:04:20.267Z</atom:updated>
            <content:encoded><![CDATA[<p>Hello Reader and welcome to the first article in the FL0 Engineering publication! Whether you are building a fintech, a health app or the next Neopets, there are some things almost EVERY startup needs…and we’re going to tackle those things in this article. We’ll begin by creating an API with Express, then we’ll connect a Postgres database and manage it with Sequelize. Finally we’ll deploy everything to a Docker container and make use of the great NodeJS and Postgres features in <a href="https://fl0.com/">FL0</a>.</p><p>The app we’ll build is going to be basic, but it will contain the essential components you need to extend it for your own project. It will include:</p><ol><li>A database for storing our startup’s products</li><li>An API endpoint for retrieving those products</li><li>A well-structured codebase including an ORM for managing database migrations, sample data and configuration.</li><li>Tools for managing the database, including separate Dev and Production environments</li></ol><p>If all goes well, you should be up and running in less than half an hour!</p><h3>Sample Repository</h3><p>You’ll find the completed codebase at this <a href="https://github.com/fl0zone/blog-express-pg-sequelize/tree/building-deploying-30-mins">link</a> on the <strong>building-deploying-30-mins </strong>branch.</p><p><a href="https://github.com/fl0zone/blog-express-pg-sequelize">GitHub - fl0zone/blog-express-pg-sequelize</a></p><h3>Prerequisites</h3><p>This article will be fairly detailed with step-by-step instructions and code snippets where possible. However, it does expect you have…</p><ul><li>A <a href="https://github.com/">Github</a> account</li><li>A <a href="https://fl0.com">FL0 account</a></li></ul><p>And the following technologies installed on your machine…</p><ul><li><a href="https://nodejs.org/en/download/">NodeJS + NPM</a></li><li><a href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">Git</a></li><li><a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li></ul><p>If you have any questions on the setup or if you think there are any steps missing in this article, please drop a comment for us below!</p><h3>Codebase Setup</h3><p>Let’s start by setting up a local repository with the Express starter code. Open up a terminal window and run the following commands:</p><pre>$ mkdir my-startup &amp;&amp; cd my-startup<br>$ npm init --yes<br>$ npm i express<br>$ npm i --save-dev nodemon dotenv</pre><p>These commands do the following:</p><ol><li>Create a new folder called “my-startup” and navigate into that folder</li><li>Initialize the folder as an NPM project so we can install dependencies</li><li>Install Express as a dependency, Nodemon (a tool for auto-reloading the server when changes are detected) and Dotenv (for loading variables from a .env file)</li></ol><p>To get a basic Express server running, create a new folder called <strong>src </strong>and a file inside called <strong>index.js</strong> and paste in the following code:</p><pre>// src/index.js<br><br>const express = require(&#39;express&#39;)<br>const app = express()<br>const port = process.env.PORT ?? 3000;<br><br>app.get(&#39;/&#39;, (req, res) =&gt; {<br>  res.send(&#39;Hello World!&#39;)<br>})<br><br>app.listen(port, () =&gt; {<br>  console.log(`Example app listening on port ${port}`)<br>})</pre><p>Edit your <strong>package.json</strong> file and add the following <strong>start</strong> and <strong>start:dev </strong>commands:</p><pre>{<br>  &quot;name&quot;: &quot;my-startup&quot;,<br>  &quot;version&quot;: &quot;1.0.0&quot;,<br>  ...<br>  &quot;scripts&quot;: {<br>    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;,<br>    &quot;start:dev&quot;: &quot;nodemon -r dotenv/config src/index.js&quot;,<br>    &quot;start&quot;: &quot;node src/index.js&quot;<br>  },<br>  ...<br>}</pre><h4>Checkpoint: The First Endpoint</h4><p>Try out your app by running npm run start:dev and visiting <a href="http://localhost:3000">http://localhost:3000</a> in your browser. See the words “Hello World”? Let’s keep going!</p><p>Before we move on to the next section, initialize your folder as a Git repo and save your changes:</p><pre>$ git init<br>$ echo &quot;node_modules\n.env&quot; &gt; .gitignore<br>$ git add .<br>$ git commit -m &quot;Set up Express framework&quot;</pre><p>The above commands achieve the following:</p><ol><li>Initialize the local Git repo (we will connect it to Github later)</li><li>Ignore the <strong>node_modules </strong>folder so it isn’t added to Git</li><li>Add all the other files to the Git stage</li><li>Commit the staged changes to the repo</li></ol><h3>Adding the Database</h3><p>In this section we’ll create a local Postgres database and install Sequelize to manage it.</p><p>In the root of your codebase, create a file called <strong>docker-compose.yml</strong> and add the following content:</p><pre>version: &#39;3&#39;<br>services:<br>  db:<br>    image: postgres:14<br>    restart: always<br>    environment:<br>      POSTGRES_USER: admin<br>      POSTGRES_PASSWORD: admin<br>      POSTGRES_DB: my-startup-db<br>    volumes:<br>      - postgres-data:/var/lib/postgresql/data<br>    ports:<br>      - 5432:5432<br>volumes:<br>  postgres-data:</pre><p>A Docker Compose file tells Docker what containers to spin up and how they should be configured. Our file contains a single service - <strong>db.</strong> It runs an image called <strong>postgres (version 14)</strong> and sets some configuration like the username and password for the database.</p><h4>Set environment variables</h4><p>Our Docker Compose file is set up to create a database with username admin, password of admin and database name of my-startup-db. We will need to tell our app how to connect to the database, so let’s do that now. Create a file in the root of your repo called .env and add the following content:</p><pre>NODE_ENV=local<br>DATABASE_URL=postgres://admin:admin@localhost:5432/my-startup-db</pre><p>The DATABASE_URL variable will be used when we set up Sequelize.</p><h4>Checkpoint: Database in a container</h4><p>Before we continue, run the following command and verify your Postgres container starts correctly!</p><pre>$ docker compose up</pre><p>If you see a message like this, you’re good to go!</p><pre>2023-01-23 23:58:33.091 UTC [1] LOG:  database system is ready to accept connections</pre><h4>Connecting with Sequelize</h4><p>Sequelize is the most popular Javascript Object Relational Mapping )(ORM) framework. Other alternatives include TypeORM and Prisma. You can use any of these with FL0, but in this article we’ll choose Sequelize.</p><p>First let’s install Sequelize and the Postgres client for NodeJS, then use the Sequelize CLI tool to initialize our project:</p><pre>$ npm install sequelize pg<br>$ cd src<br>$ npx sequelize-cli init --config=config/index.js</pre><p>The npx sequelize-cli init command bootstraps our project with a few folders and files:</p><ul><li><strong>config:</strong> a place to store connection details for our database</li><li><strong>migrations:</strong> A folder for storing database migration scripts, used to apply changes to the schema</li><li><strong>models:</strong> Every table in the database will be defined as a “model” and stored in this folder</li><li><strong>seeders:</strong> We can bootstrap our tables with sample data by adding scripts to this folder</li></ul><p>Next, let’s alter the src/config/index.js file to point to our database. Replace its content with the following:</p><pre>module.exports = {<br>  &quot;local&quot;: {<br>    &quot;use_env_variable&quot;: &quot;DATABASE_URL&quot;<br>  },<br>  &quot;development&quot;: {<br>    &quot;use_env_variable&quot;: &quot;DATABASE_URL&quot;<br>  },<br>  &quot;production&quot;: {<br>    &quot;use_env_variable&quot;: &quot;DATABASE_URL&quot;<br>  }<br>}</pre><p>This tells Sequelize to read from the .env file we created earlier. To test our connection, alter the index.js file to look like this:</p><pre>const express = require(&#39;express&#39;)<br>const { sequelize } = require(&#39;./models&#39;);<br>const app = express()<br>const port = process.env.PORT ?? 3000;<br><br>app.get(&#39;/&#39;, (req, res) =&gt; {<br>  res.send(&#39;Hello World!&#39;)<br>})<br><br>app.listen(port, async () =&gt; {<br>  console.log(`Example app listening on port ${port}`)<br>  try {<br>    await sequelize.authenticate();<br>    await sequelize.sync({ force: true});<br>    console.log(&#39;Connection has been established successfully.&#39;);<br>  } catch (error) {<br>    console.error(&#39;Unable to connect to the database:&#39;, error);<br>  }<br>})</pre><p>These changes import the sequelize object from our new src/models/index.js file and attempt to connect to the database.</p><h4>Checkpoint:</h4><p>Run npm run start:dev and make sure you can see a message that says “Connection has been established successfully”. If you see any errors:</p><ol><li>Make sure your database container is still running with docker compose up</li><li>Make sure your environment variables are loaded properly. You can verify this by adding console.log(JSON.stringify(process.env)) to your src/index.js file and looking for the DATABASE_URL key</li></ol><p>Now is a good time to commit your changes!</p><pre>$ git add . &amp;&amp; git commit -m &quot;Set up Sequelize connection&quot; </pre><h3>Creating the first database models</h3><p>Now that Sequelize is set up we can start populating the database with some tables and values. Let’s use the Sequelize CLI tool to create our first model. Run the following script in your Terminal from the src folder:</p><pre>$ npx sequelize-cli model:generate --name=Product --attributes=sku:string,name:string,description:text,isPublished:boolean,imageURL:string,price:decimal,weight:decimal</pre><p>This will automatically generate src/models/product.js and a corresponding file in the migrations folder. Next, we’ll define some seed data to populate the Product table with some information:</p><pre>$ npx sequelize-cli seed:generate --name=products</pre><p>Open the created file under the seeders folder and replace it with the following content:</p><pre>&#39;use strict&#39;;<br><br>/** @type {import(&#39;sequelize-cli&#39;).Migration} */<br>module.exports = {<br>  async up(queryInterface, Sequelize) {<br>    await queryInterface.bulkInsert(&#39;Products&#39;, [{<br>      sku: &#39;STR0001&#39;,<br>      name: &#39;Three-lobed fidget spinner&#39;,<br>      description: &#39;A fidget spinner is a toy that consists of a ball bearing in the center of a multi-lobed (typically two or three) flat structure made from metal or plastic designed to spin along its axis with pressure. Fidget spinners became trending toys in 2017, although similar devices had been invented as early as 1993.[1]&#39;,<br>      imageURL: &#39;https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Fidget_spinner_red%2C_cropped.jpg/1280px-Fidget_spinner_red%2C_cropped.jpg&#39;,<br>      isPublished: true,<br>      price: 12.95,<br>      weight: 0.1,<br>      createdAt: new Date(),<br>      updatedAt: new Date(),<br>    },<br>    {<br>      sku: &#39;STR0002&#39;,<br>      name: &#39;World-map stress ball&#39;,<br>      description: &#39;A stress ball or hand exercise ball is a malleable toy, usually not more than 7 cm in diameter, which is squeezed in the hand and manipulated by the fingers, ostensibly to relieve stress and muscle tension or to exercise the muscles of the hand. Patrick Hummel is widely understood to have created the stress ball in central Indiana in the mid-1980s.&#39;,<br>      imageURL: &#39;https://upload.wikimedia.org/wikipedia/commons/thumb/7/76/Earth_globe_stress_ball.jpg/220px-Earth_globe_stress_ball.jpg&#39;,<br>      isPublished: true,<br>      price: 4.99,<br>      weight: 0.05,<br>      createdAt: new Date(),<br>      updatedAt: new Date(),<br>    },<br>    {<br>      sku: &#39;STR0003&#39;,<br>      name: &#39;Metallic slinky&#39;,<br>      description: &#39;The Slinky is a precompressed[clarification needed] helical spring toy invented by Richard James in the early 1940s. It can perform a number of tricks, including travelling down a flight of steps end-over-end as it stretches and re-forms itself with the aid of gravity and its own momentum, or appear to levitate for a period of time after it has been dropped. These interesting characteristics have contributed to its success as a toy in its home country of the United States, resulting in many popular toys with slinky components in a wide range of countries.&#39;,<br>      imageURL: &#39;https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/2006-02-04_Metal_spiral.jpg/200px-2006-02-04_Metal_spiral.jpg&#39;,<br>      isPublished: true,<br>      price: 15.45,<br>      weight: 0.2,<br>      createdAt: new Date(),<br>      updatedAt: new Date(),<br>    }<br>  ], {});<br>  },<br><br>  async down(queryInterface, Sequelize) {<br>  }<br>};</pre><p>The code above inserts three sample products for us to work with. To import the data, make sure your app is running with npm run start:dev and then in a new terminal window run:</p><pre>$ npx sequelize-cli db:seed:all --config=src/config/index.js</pre><p>Hopefully you see some output that looks like this!</p><pre>== 20230124200930-products: migrating =======<br>== 20230124200930-products: migrated (0.031s)</pre><h3>Connecting the Database and API</h3><p>Now that we have a database with some product records, it’s time to create an API to expose this information. Update src/index.js with the following content:</p><pre>const express = require(&#39;express&#39;)<br>const { sequelize, Product } = require(&#39;./models&#39;);<br>const app = express()<br>const port = process.env.PORT ?? 3000;<br><br>app.get(&#39;/&#39;, (req, res) =&gt; {<br>  res.send(&#39;Hello World!&#39;)<br>})<br><br>app.get(&#39;/products&#39;, async (req, res) =&gt; {<br>  const products = await Product.findAll();<br>  res.json(products);<br>})<br><br>app.listen(port, async () =&gt; {<br>  console.log(`Example app listening on port ${port}`)<br>  try {<br>    await sequelize.authenticate();<br>    await sequelize.sync({ force: true});<br>    console.log(&#39;Connection has been established successfully.&#39;);<br>  } catch (error) {<br>    console.error(&#39;Unable to connect to the database:&#39;, error);<br>  }<br>})</pre><p>Notice the new /products route, and the Product object being imported from the Sequelize model file. Verify this is working by hitting <a href="http://localhost:3000/products">http://localhost:3000/products</a> and ensuring you see the product data returned as JSON.</p><p>Commit your changes and continue reading!</p><pre>$ git add . &amp;&amp; git commit -m &quot;Created GET /products endpoint&quot;</pre><h3>Deploying the app</h3><p>Now that we’ve got a working API and database we can deploy it to a server! In this article we’re using FL0, a platform that makes it really easy to deploy NodeJS apps to a containerized infrastructure, complete with database.</p><h4>Pushing to Github</h4><p>Up until now we’ve been committing changes to a local repo that only exists on our machine. Let’s link that to a Github repo so it can be stored in the cloud. In Github, create a new empty repo with no license or .gitignore file. If all goes well, you should see a screen like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rzszSacwxIcMcSw7aMBFKA.png" /></figure><p>We need to follow the steps under the heading “…or push an existing repository from the command line”. <em>Note: I haven’t included the commands here because they will be specific to your own repo and may use HTTPS instead of SSH like mine does. It’s better to copy+paste from the Github page than this article for this step.</em></p><p>If successful, you should be able to refresh the Github page and see your code:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JpFkc4UUHRSuTrBKIHqqQg.png" /></figure><h4>Configuring FL0</h4><p>In FL0, create a new empty project and then add a Postgres database. You can call it anything you like.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X4px31EzNa4mIffz8Sl4XQ.png" /></figure><p>Next, add a new Service and connect to your Github account:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uofgY9b49b_L4Vmo6mshvA.png" /></figure><p>You should see a window asking you to authorize FL0 to connect to your account:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wl0qFip_gHQ8qrh0AYCECQ.png" /></figure><p>When you are returned to FL0, your repos will be available to deploy through the user interface. Click the “Connect” button to continue. On the next page, you have the option to specify a Service Name and set Environment Variables. This is where we need to insert our Database URL.</p><p>Click the “+ Environment Variables” button and in the popup window that appears, select “Import database credentials”:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JZWQOvzzMLIxDSuq9aBCFQ.png" /></figure><p>Make sure you see a row called DATABASE_URL before continuing.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l3MYTzSESms5V0btJIqkDg.png" /></figure><p>Save your changes and click the “Deploy” button!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/639/1*FdVRWKq3_OPY1G6fhZnjKQ.png" /></figure><p>This will trigger a new build to begin. Click on your new Service and navigate to the Deployments tab to see how it’s progressing:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vZHCeXbOu2kNPL6wYIFeLw.png" /></figure><p>Once complete, go to the Overview tab and copy the URL:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YRO9HQtpay4ICKrp0glp6A.png" /></figure><p>Open it up in a browser and you should see the familiar “Hello World” message! Add a /products onto the end and you should see…nothing…yet! This is because our new FL0 database is still empty and we need to run the Seed script to insert the sample data.</p><h4>Pointing the Sequelize CLI at FL0</h4><p>Earlier we created a .env file that contained a DATABASE_URL variable instructing Sequelize to connect to our Docker container. Let’s edit the file to include a new variable, our FL0 database URL. You can find the Database URL in FL0 under the Connection Info tab of the Postgres resource.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*d5QvO5yX0hKtJRU0Xl7hxw.png" /></figure><p>Copy the .env and call it .env.dev and updated it as follows:</p><pre>NODE_ENV=development<br>DATABASE_URL=&lt;YOUR FL0 DATABASE_URL&gt;</pre><p>Adding this will let us run the Sequelize CLI against different environments. Try this:</p><pre>$ npx dotenv-cli -e .env.dev -- npx sequelize-cli db:seed:all --config=src/config/index.js</pre><p>Notice the dotenv-cli -e .env.dev option telling Sequelize to use FL0 instead of our local container. Go back to your browser and check the /products URL again. Hopefully you see some product JSON! Using FL0’s deployment features you can easily replicate your container to the Production environment with a completely separate database.</p><p>And there you have it folks, the building blocks for a successful NodeJS project! With the knowledge and skills you’ve gained from this article, you’re ready to bring your next big idea to life. We’d love to hear what you’re building in the comments below! We hope this article brought a smile to your face and a little inspiration to help tackle help your next project. Until next time, happy coding!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tmWFSS88RNmSx7I3i4WgPA.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f0624174e738" width="1" height="1" alt=""><hr><p><a href="https://medium.com/fl0-engineering/building-a-software-startup-on-fl0-part-1-f0624174e738">Building and deploying a NodeJS + Postgres API in less than 30 minutes</a> was originally published in <a href="https://medium.com/fl0-engineering">FL0 Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>