<?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[Stories by Lucas Santos on Medium]]></title>
        <description><![CDATA[Stories by Lucas Santos on Medium]]></description>
        <link>https://medium.com/@khaosdoctor?source=rss-84c42a22cef7------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*2ughb78aGoOjiMIABAxwiQ.jpeg</url>
            <title>Stories by Lucas Santos on Medium</title>
            <link>https://medium.com/@khaosdoctor?source=rss-84c42a22cef7------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 09 Jun 2026 03:21:12 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@khaosdoctor/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Best practices for HarperDB projects using TypeScript]]></title>
            <link>https://blog.stackademic.com/best-practices-for-harperdb-projects-using-typescript-758f63d84885?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/758f63d84885</guid>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[harperdb]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Wed, 04 Oct 2023 19:52:21 GMT</pubDate>
            <atom:updated>2023-10-10T01:52:38.318Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JSqzyo1sYK-k101n1wl1cQ.png" /></figure><p>When you are working with HarperDB, you can use TypeScript to improve your development experience. This article will show you some best practices to use TypeScript with HarperDB. We’ll talk about folder structures, best practices of code, and how to best set your project to make the most of HarperDB.</p><h3>Before you start</h3><p>Make sure you hop into the <a href="https://docs.harperdb.io/docs/install-harperdb">HarperDB Documentation</a> to know how to install it locally and get started. You can also use the <a href="https://harperdb.io/">HarperDB Cloud</a> to get started quickly. But I’ll assume you already have your instance running.</p><blockquote><strong><em>Tip:</em></strong><em> You can check some of my other tutorials </em><a href="https://www.harperdb.io/author/lucas-santos"><em>here</em></a><em> or </em><a href="https://www.harperdb.io/development/tutorials/harperdb-authentication-with-oauth"><em>in this particular community article</em></a><em> to know how to set up your infrastructure locally, with Kubernetes, or in the cloud.</em></blockquote><p>This repository is located at <a href="https://github.com/HarperDB-Add-Ons/hdb-typescript-best-practices">the HarperDB Community GitHub</a>. You can clone it and follow along with the article. We’ll be using the cloud version of the database but, in the repository, you’ll find a docker-compose.yml file that you can use to run it locally with docker compose up.</p><h3>Setting up Node and TypeScript</h3><p>To use TypeScript you need Node.js installed, be sure to use the latest LTS version. You can check it by running node -v in your terminal. If you don’t have it installed, you can download it <a href="https://nodejs.org/en/download/">here</a>, or use a version manager like <a href="https://asdf-vm.com/#/core-manage-asdf-vm">asdf</a>, <a href="https://github.com/nvm-sh/nvm">nvm</a>, or even <a href="https://volta.sh/">volta</a>.</p><blockquote><em>I’m using version 20.7.0 of Node.js, but you can use any version above 18 at the time of writing this article.</em></blockquote><p>Let’s create a new directory (you can name whatever you want) and, inside it, we’ll run the command npm init -y. This will generate a new package.json file with the default values. Now, let’s install TypeScript as a development dependency by running npm install --save-dev typescript. This will install the latest version of TypeScript in your project.</p><blockquote><strong><em>Tip:</em></strong><em> You can check the latest version of TypeScript </em><a href="https://www.npmjs.com/package/typescript"><em>here</em></a><em>. I’m using version 5.2.2</em></blockquote><p>We’ll also install the external types for Node.js by running npm install --save-dev @types/node. This will allow us to use the Node.js types in our project.</p><p>Lastly, we will add a support package called <a href="https://npm.im/tsx">tsx</a>, which will allow us to develop our application without having to compile it every time we change something. We’ll install it by running npm install --save-dev tsx.</p><p>Let’s then bootstrap typescript with npx tsc --init, this will generate a tsconfig.json file with all the definitions TS needs. We’ll need to change some values there, though. So let’s open it, remove all the values and leave it like this:</p><pre>{<br>  &quot;compilerOptions&quot;: {<br>    &quot;target&quot;: &quot;ESNext&quot;,<br>    &quot;module&quot;: &quot;NodeNext&quot;,<br>    &quot;outDir&quot;: &quot;./dist&quot;,<br>    &quot;esModuleInterop&quot;: true,<br>    &quot;forceConsistentCasingInFileNames&quot;: true,<br>    &quot;strict&quot;: true,<br>    &quot;skipLibCheck&quot;: true<br>  }<br>}</pre><p>We’ll also be using ESModules, so this needs us to change the type key in our package.json file to module. This will allow us to use the import syntax in our code, let’s also add a run script that will allow us to run our compiled code. Our package.json file will be like this:</p><pre>{<br>  &quot;name&quot;: &quot;hdb-typescript-best-practices&quot;,<br>  &quot;version&quot;: &quot;0.0.1&quot;,<br>  &quot;description&quot;: &quot;&quot;,<br>  &quot;main&quot;: &quot;index.js&quot;,<br>  &quot;type&quot;: &quot;module&quot;,<br>  &quot;scripts&quot;: {<br>    &quot;start&quot;: &quot;node --env-file=.env dist/index.js&quot;,<br>    &quot;start:dev&quot;: &quot;NODE_OPTIONS=&#39;--loader=tsx&#39; node --env-file=.env src/index.ts&quot;<br>  },<br>  &quot;keywords&quot;: [],<br>  &quot;author&quot;: &quot;Lucas Santos &lt;hello@lsantos.dev&gt; (https://lsantos.dev/)&quot;,<br>  &quot;license&quot;: &quot;GPL-3.0&quot;,<br>  &quot;devDependencies&quot;: {<br>    &quot;@types/node&quot;: &quot;^20.7.1&quot;,<br>    &quot;tsx&quot;: &quot;^3.13.0&quot;,<br>    &quot;typescript&quot;: &quot;^5.2.2&quot;<br>  }<br>}</pre><h3>Testing the setup</h3><p>Now, let’s create a src folder and a index.ts file inside it. This will be our entrypoint for the application. Let’s also add a dist and node_modules folder to our .gitignore file (if you haven’t added already), so we don’t commit the compiled code.</p><p>Node.js from version 20.6 up <a href="https://nodejs.org/en/blog/release/v20.6.0">had built-in env file support</a>, so we’ll take advantage of this to store our secrets in a .env file, this is the first best practice:</p><blockquote><strong><em>Best Practice:</em></strong><em> Never commit secrets to your repository. Use a </em><em>.env file to store them and add it to your </em><em>.gitignore file.</em></blockquote><p>Let’s create a .env file and add the following content:</p><pre>EXAMPLE=Test environment</pre><p>Now, let’s add the following code to our index.ts file:</p><pre>export async function main() {<br>  console.log(&#39;Hello world!&#39;)<br>  console.log(process.env.EXAMPLE)<br>}<br>await main()</pre><p>Now run the setup with npm run start:dev, you should see the following output:</p><pre>$ npm run start:dev<br>Hello World!<br>Test environment</pre><p>This means that everything is correctly set up!</p><blockquote><strong><em>Tip</em></strong><em>: You can also run the file manually through compilation, to do this, you need to run </em><em>npx tsc in the root directory, then run </em><em>node --env-file=.env dist/index.js. This will have the same result. You can even add a </em><em>build script that will have </em><em>tsc as a command, so you can run </em><em>npm run build and then </em><em>npm run start to run the code.</em></blockquote><h3>Setting the environments and the database</h3><p>Now that we know everything is working, let’s hop to the <a href="https://studio.harperdb.io/">HarperDB Studio</a> and create our schema. We’ll create a new schema called todo and a table called todo_items. This will be our table that will store the to-do items.</p><p>The hash of the table will be the id column:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/340/1*I1xYaURJnxTO45CIGaxRxw.png" /></figure><p>Harper has a malleable schema, so we don’t need to define all the properties, just the initial hash property, all the others will be added as we create the objects.</p><p>Let’s set our environment variables in the .env file:</p><pre>HDB_HOST=https://your-instance.harperdbcloud.com<br>HDB_USERNAME=your username<br>HDB_PASSWORD=your password<br>HDB_SCHEMA=todo<br>HDB_TABLE=todo_items</pre><h3>Separating Layers</h3><p>For our application, we’ll be creating the simple glorious to-do list application. This application will have the following features:</p><ul><li>Create a new to-do item</li><li>List all to-do items</li><li>Mark a to-do item as done</li><li>Delete a to-do item</li></ul><p>It’s a simple application, but it will allow us to explore some of the best practices of using TypeScript with HarperDB.</p><p>We’ll also use a layered architecture to separate our code. This will allow us to have a better separation of concerns and make our code more maintainable. Layered architectures allow TypeScript to shine because we can use interfaces to define our contracts and make sure that our code is following the correct structure.</p><p>We will have at least three layers:</p><ul><li><strong>Presentation layer</strong>: This is the layer that interfaces with the user. It can be a CLI, a web application, or even a mobile application. This layer will be responsible for receiving the user input and sending it to the next layer. In our case, we’ll have the API, which will be a REST API that will receive the user input and send it to the next layer. But this allows us to separate the real application logic from the presentation layer, which makes us able to shift the presentation layer to another technology without having to change the application logic. So we could add a CLI, a GraphQL endpoint, gRPC, or anything else without having to change the underlying structure.</li><li><strong>Domain or service layer</strong>: This is the layer that will have the business logic of our application. It will be responsible for receiving the validated user input and acting upon it. It will also be responsible for receiving the data from the data layer and transforming it into the correct format for the presentation layer. This layer will be the one that will have the most logic, and can be separated in multiple parts. The domain layer can be accessed by any other layers as it’s a core part of the system.</li><li><strong>Data layer</strong>: As the name says, this is the layer that will be responsible for the data, data can come from any source, a database, an external client, etc. It will be responsible for receiving the data from the domain layer and transforming it into the correct format for the database. It will also be responsible for receiving the data from the database and transforming it into the correct format for the domain layer. This layer will be the one that will have the least logic, but it’s the most important, because this is where we will add our database logic, and our database communication.</li></ul><p>This is pretty close to the famous <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC Architecture</a>, but it takes a bit of the definitions from <a href="https://en.wikipedia.org/wiki/Domain-driven_design">Domain Driven Design</a> and <a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">Clean Architecture</a>.</p><h3>The domain layer</h3><p>Before creating any layers, we need to model our domain object, which is the to-do item. This will allow us to have a better understanding of what we need to do and how we need to do it. It will allow us to understand what is the shape of our object and how we will manipulate it.</p><p>Let’s start with a domain folder inside src, there we will create a file called TodoItem.ts. This will be our domain object, so let’s add the following code to it:</p><pre>import { randomUUID } from &#39;node:crypto&#39;<br><br>export class TodoItem {<br>  constructor(<br>    public title: string,<br>    public dueDate: Date,<br>    readonly id: string = randomUUID(),<br>    public completed: boolean = false,<br>    readonly createdAt: Date = new Date()<br>  ) {}<br>}</pre><p>This is a simple class that will represent our to-do item. It has a constructor that receives the title and due date, and sets the other properties. The id is a random UUID, the completed is false by default, and the createdAt is the current date.</p><p>So we can now create a new to-do item by running new TodoItem(&#39;My first to-do item&#39;, new Date()). This will create a new to-do item with the title My first to-do item and the due date as the current date.</p><blockquote><strong><em>Tip:</em></strong><em> You can check the </em><a href="https://nodejs.org/api/crypto.html#crypto_crypto_randomuuid_options"><em>Node.js documentation</em></a><em> to know more about the </em><em>randomUUID function.</em></blockquote><p>Let’s add some features to our domain object to make it more useful. We’ll add a toJSON method that will return the object as a JSON string, and a fromObject static method that will receive an object that matches out Data Object and return a new instance of the class.</p><p>For this we will need to create a schema that we can compare and validate our object against, this is the perfect use case for <a href="https://zod.dev/">Zod</a>! Zod is a TypeScript-first schema validation library that allows us to create schemas and validate our objects against them. Let’s install it by running npm install --save zod.</p><p>To define the schema we have two options:</p><ol><li>We define it in a new file and import it in our domain object</li><li>We define it inside the domain object</li></ol><p>I personally prefer the second option since both the schema and the domain object are tightly coupled and part of the same object, so it makes sense to have them in the same file. But you can choose the one that makes more sense to you.</p><p>Let’s add the following code to the top of our domain object:</p><pre>import { z } from &#39;zod&#39;<br><br>const todoItemSchema = z.object({<br>  title: z.string(),<br>  dueDate: z.date(),<br>  id: z.string().uuid(),<br>  completed: z.boolean().default(false),<br>  createdAt: z.date().default(new Date())<br>})<br><br>export type TodoObjectType = z.infer&lt;typeof TodoObjectSchema&gt;</pre><blockquote><strong><em>Note</em></strong><em>: It’s also possible to somewhat mimic what we are doing in the </em><em>z.infer&lt;typeof TodoObjectSchema&gt; by using the </em><em>InstanceType utility type, but then we would need to strip out the methods from the type, so I prefer to use the </em><em>infer method to also create a clear sense of separation of what is our domain object (the class) and also the data transfer object (the JSON)</em></blockquote><p>We will then use this schema to make sure our object is a valid to-do item. Let’s change the fromJSON function in our domain object:</p><pre>static fromObject(todoObject: TodoObjectType): InstanceType&lt;typeof TodoItem&gt; {<br>  TodoObjectSchema.parse(todoObject) // This will throw an error if the object is not valid<br>  return new TodoItem(todoObject.title, todoObject.dueDate, todoObject.id, todoObject.completed, todoObject.createdAt)<br>}</pre><p>This will parse the JSON string and return a new instance of the class. We can also add a toObject to turn the class into a serializable object, and a toJSON method that will return the JSON string, this will be useful when we need to send the object to the database. Let’s add the following code to our domain object:</p><pre>toObject() {<br>  return JSON.stringify({<br>    title: this.title,<br>    dueDate: this.dueDate,<br>    id: this.id,<br>    completed: this.completed,<br>    createdAt: this.createdAt<br>  })<br>}<br><br>toJSON() {<br>  return JSON.stringify(this.toObject())<br>}</pre><blockquote><strong><em>Tip</em></strong><em>: One other thing that we can do here is to not use the </em><em>toObject method and instead use the shorthand </em><em>{ ...item }, which will automatically convert it into an object. But I prefer to have a method that I can call to make it more explicit. You can also return </em><em>{ ...this }</em></blockquote><p>Our final domain object will look like this:</p><pre>import { randomUUID } from &#39;node:crypto&#39;<br>import { z } from &#39;zod&#39;<br><br>const TodoObjectSchema = z<br>  .object({<br>    title: z.string(),<br>    dueDate: z.date({ coerce: true }),<br>    id: z.string().uuid().readonly(),<br>    completed: z.boolean().default(false),<br>    createdAt: z.date({ coerce: true }).default(new Date()).readonly()<br>  })<br>  .strip()<br>export type TodoObjectType = z.infer&lt;typeof TodoObjectSchema&gt;<br><br>export class TodoItem {<br>  constructor(<br>    public title: string,<br>    public dueDate: Date,<br>    readonly id: string = randomUUID(),<br>    public completed: boolean = false,<br>    readonly createdAt: Date = new Date()<br>  ) {}<br><br>  toObject() {<br>    return JSON.stringify({<br>      title: this.title,<br>      dueDate: this.dueDate,<br>      id: this.id,<br>      completed: this.completed,<br>      createdAt: this.createdAt<br>    })<br>  }<br><br>  toJSON() {<br>    return JSON.stringify(this.toObject())<br>  }<br><br>  static fromObject(todoObject: TodoObjectType): InstanceType&lt;typeof TodoItem&gt; {<br>    TodoObjectSchema.parse(todoObject)<br>    return new TodoItem(todoObject.title, todoObject.dueDate, todoObject.id, todoObject.completed, todoObject.createdAt)<br>  }<br>}</pre><h3>The data layer</h3><p>Now that we have our domain object, we can start creating our data layer. This layer will be responsible for communicating with the database and transforming the data from the database into the correct format for the domain layer. It will also be responsible for transforming the data from the domain layer into the correct format for the database.</p><p>Since we’re using Harper, all our communication with the DB is done through an API! Which is extremely useful because we don’t need to set up complicated drivers or anything like that. So let’s create a new file in a data folder called TodoItemClient.ts. This will be our HarperDB client.</p><blockquote><strong><em>Tip</em></strong><em>: It’s a best practice to name external APIs as ‘clients’, if we had any other data layer, let’s say a queue system that’s not connected through an API, we could just name it </em><em>queueAdapter or </em><em>queue and it would be fine. This is not a rule, but I personally think it’s easier to understand what the file is doing, if an external agent or an internal driver.</em></blockquote><p>Our client is an HTTP API, so let’s use the native fetch from node to communicate with it (remember that the fetch api is only available in node from version 18 and up). Let’s add the following code to our TodoItemClient.ts file:</p><pre>import { TodoItem, TodoObjectType } from &#39;../domain/TodoItem.js&#39;<br><br>export class TodoItemClient {<br>  #defaultHeaders: Record&lt;string, string&gt; = {<br>    &#39;Content-Type&#39;: &#39;application/json&#39;<br>  }<br>  credentialsBuffer: Buffer<br><br>  constructor(<br>    private readonly url: string,<br>    private readonly schema: string,<br>    private readonly table: string,<br>    credentials: { username: string; password: string }<br>  ) {<br>    this.credentialsBuffer = Buffer.from(`${credentials.username}:${credentials.password}`)<br>    this.#defaultHeaders[&#39;Authorization&#39;] = `Basic ${this.credentialsBuffer.toString(&#39;base64url&#39;)}`<br>  }<br><br>  async upsert(data: TodoItem) {<br>    const payload = {<br>      operation: &#39;upsert&#39;,<br>      schema: this.schema,<br>      table: this.table,<br>      records: [data.toObject()]<br>    }<br>    const response = await fetch(this.url, {<br>      method: &#39;POST&#39;,<br>      headers: this.#defaultHeaders,<br>      body: JSON.stringify(payload)<br>    })<br>    if (!response.ok) {<br>      throw new Error(response.statusText)<br>    }<br>    return data<br>  }<br><br>  async delete(id: string) {<br>    const payload = {<br>      operation: &#39;delete&#39;,<br>      schema: this.schema,<br>      table: this.table,<br>      hash_values: [id]<br>    }<br>    const response = await fetch(this.url, {<br>      method: &#39;POST&#39;,<br>      headers: this.#defaultHeaders,<br>      body: JSON.stringify(payload)<br>    })<br>    if (!response.ok) {<br>      throw new Error(response.statusText)<br>    }<br>  }<br><br>  async findOne(id: string) {<br>    const payload = {<br>      operation: &#39;search_by_hash&#39;,<br>      schema: this.schema,<br>      table: this.table,<br>      hash_values: [id],<br>      get_attributes: [&#39;*&#39;]<br>    }<br>    const response = await fetch(this.url, {<br>      method: &#39;POST&#39;,<br>      headers: this.#defaultHeaders,<br>      body: JSON.stringify(payload)<br>    })<br>    if (!response.ok) {<br>      throw new Error(response.statusText)<br>    }<br>    const data = (await response.json()) as TodoObjectType[]<br>    if (data[0] &amp;&amp; Object.keys(data[0]).length &gt; 0) {<br>      return TodoItem.fromObject(data[0])<br>    }<br>    return null<br>  }<br><br>  async listByStatus(completed = true) {<br>    const payload = {<br>      operation: &#39;search_by_value&#39;,<br>      schema: this.schema,<br>      table: this.table,<br>      search_attribute: &#39;completed&#39;,<br>      search_value: completed,<br>      get_attributes: [&#39;*&#39;]<br>    }<br>    const response = await fetch(this.url, {<br>      method: &#39;POST&#39;,<br>      headers: this.#defaultHeaders,<br>      body: JSON.stringify(payload)<br>    })<br>    if (!response.ok) {<br>      throw new Error(response.statusText)<br>    }<br>    const data = (await response.json()) as TodoObjectType[]<br>    return data.map((todoObject) =&gt; TodoItem.fromObject(todoObject))<br>  }<br>}</pre><p>As you can see this has all the methods we need to communicate with the database. We have the upsert method that will create or update a record, the delete method that will delete a record, the findOne method that will find a record by its hash, and the listByStatus method that will list all the records that match a certain value.</p><blockquote><strong><em>Tip</em></strong><em>: You can check the </em><a href="https://api.harperdb.io/#25977fef-53e8-40bf-a26a-43369bc3721c"><em>HarperDB documentation</em></a><em> to know more about the operations.</em></blockquote><h3>The service layer</h3><p>The service layer will be the glue between all the other layers. In an application that’s this simple, it’s usually not very useful, but it’s a good practice to have it, so we can add more logic to it later on. Let’s create a new folder called services and a new file called TodoItemService.ts. This will be our service layer.</p><p>The service layer will receive sanitized user input, perform any business logic and then send it to the data layer. It will also receive data from the data layer and transform it into the correct format for the presentation layer.</p><p>Let’s add the following code to our TodoItemService.ts file:</p><pre>import { TodoItemClient } from &#39;../data/TodoItemClient.js&#39;<br>import { TodoItem, TodoObjectType } from &#39;../domain/TodoItem.js&#39;<br><br>export class TodoItemService {<br>  #client: TodoItemClient<br><br>  constructor(client: TodoItemClient) {<br>    this.#client = client<br>  }<br><br>  async findOne(id: string) {<br>    return this.#client.findOne(id)<br>  }<br><br>  async findAll() {<br>    return [...(await this.findPending()), ...(await this.findCompleted())]<br>  }<br><br>  async findCompleted() {<br>    return this.#client.listByStatus(true)<br>  }<br><br>  async findPending() {<br>    return this.#client.listByStatus(false)<br>  }<br><br>  async create(todoItem: TodoObjectType) {<br>    const todo = new TodoItem(todoItem.title, todoItem.dueDate)<br>    return this.#client.upsert(todo)<br>  }<br><br>  async update(todoItem: TodoObjectUpdateType) {<br>    const todo = await this.#client.findOne(todoItem.id ?? &#39;&#39;)<br>    if (!todo) {<br>      throw new Error(&#39;Todo not found&#39;)<br>    }<br>    todo.completed = todoItem.completed ?? todo.completed<br>    todo.dueDate = todoItem.dueDate ?? todo.dueDate<br>    todo.title = todoItem.title ?? todo.title<br>    return this.#client.upsert(todo)<br>  }<br><br>  async delete(id: string) {<br>    return this.#client.delete(id)<br>  }<br>}</pre><p>See that, while in the client implementation on a lower layer, we have a more generic logic which finds by status. In the service, we already separated the commands into finding pending and completed items.</p><p>Also, we implemented a findAll method which calls the listByStatus method twice and then merges the results. This is a good example of how we can use the service layer to add more logic to our application without actually adding more logic to the data layer.</p><p>Another important aspect to notice is how the service layer already assumes that all the data will be sanitized. This is a good practice because it allows us to have a better separation of concerns. The presentation layer should be the one validating the data in the route, and then sending it to the service layer. The service layer should assume that the data is already validated and sanitized.</p><blockquote><strong><em>Tip</em></strong><em>: In any case, we have already implemented another level of validations in our Domain object when we are receiving objects because TypeScript will only enforce them at the compile time, so we can be sure that the data is valid.</em></blockquote><p>The last thing to notice is that, now that we have moved up a level, the service layer is taking as a parameter, the layer below, which means that we need to pass an instance of the data layer client to the service layer. This is called <strong>inversion of control</strong> and it’s a part of the <a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle">Dependency Inversion Principle</a>.</p><p>This principle states that the higher level layers should not depend on the lower level layers, but instead, they should depend on abstractions. In our case, the service layer depends on the data layer, but it doesn’t depend on the implementation of the data layer, it depends on the abstraction of the data layer, which is the client.</p><p>We will do the same with the presentation layer.</p><h3>The presentation layer</h3><p>Now that we have our data layer, we can start creating our presentation layer. This layer will be responsible for receiving the user input and sending it to the next layer. In our case, we’ll have the API, which will be a REST API that will receive the user input and send it to the next layer.</p><p>Let’s create a new folder called presentation and a new file called restAPI.ts. This will be our REST interface.</p><p>Usually we use a web framework to avoid having to recreate everything. In this example we’ll use a very fast framework called <a href="https://hono.dev/">Hono</a>, just to be away from <a href="https://expressjs.com/">Express</a> for a while.</p><p>Install it and then it’s adapter for Node.js by running by running npm install --save hono @hono/node-server.</p><p>The implementation of the service layer usually falls into the <a href="https://en.wikipedia.org/wiki/Factory_method_pattern">Factory Pattern</a>, which is a creational pattern that allows us to create objects without having to know the implementation details. In our case, we’ll use a factory to create the Rest API client so we can receive (via dependency injection) the service layer.</p><p>This translates to something like this:</p><pre>import { Hono } from &#39;hono&#39;<br>import { TodoItemService } from &#39;../services/TodoItemService.js&#39;<br><br>export async function restAPIFactory(service: TodoItemService) {<br>  const app = new Hono()<br>  app.get(&#39;/api/todos/:id&#39;, async (c) =&gt; {})<br>  app.get(&#39;/api/todos&#39;, async (c) =&gt; {})<br>  app.post(&#39;/api/todos&#39;, async (c) =&gt; {})<br>  app.put(&#39;/api/todos/:id&#39;, async (c) =&gt; {})<br>  app.delete(&#39;/api/todos/:id&#39;, async (c) =&gt; {})<br>  return app<br>}</pre><p>This is a very simple factory that receives the service layer and returns a new instance of the Rest API client. We’ll implement the routes in a moment, but first, we’ll go back to our index.ts file in the src directory. This will be our entrypoint for the application.</p><p>There we’ll initiate the environment variables, as well as the database client and the service layer. Let’s add the following code to our index.ts file:</p><pre>import { z } from &#39;zod&#39;<br>import { TodoItemClient } from &#39;./data/TodoItemClient.js&#39;<br>import { TodoItemService } from &#39;./services/TodoItemService.js&#39;<br>import { restAPIFactory } from &#39;./presentation/api.js&#39;<br>import { serve } from &#39;@hono/node-server&#39;<br>const conf = {<br>  host: process.env.HDB_HOST,<br>  credentials: {<br>    username: process.env.HDB_USERNAME,<br>    password: process.env.HDB_PASSWORD<br>  },<br>  schema: process.env.HDB_SCHEMA,<br>  table: process.env.HDB_TABLE<br>}<br><br>const EnvironmentSchema = z.object({<br>  host: z.string(),<br>  credentials: z.object({<br>    username: z.string(),<br>    password: z.string()<br>  }),<br>  schema: z.string(),<br>  table: z.string()<br>})<br><br>export type EnvironmentType = z.infer&lt;typeof EnvironmentSchema&gt;<br><br>export default async function main() {<br>  const parsedSchema = EnvironmentSchema.parse(conf)<br>  const DataLayer = new TodoItemClient(<br>    parsedSchema.host,<br>    parsedSchema.schema,<br>    parsedSchema.table,<br>    parsedSchema.credentials<br>  )<br>  const ServiceLayer = new TodoItemService(DataLayer)<br>  const app = await restAPIFactory(ServiceLayer)<br>  serve({ port: 3000, fetch: app.fetch }, console.log)<br>}<br><br>await main()</pre><blockquote><strong><em>Note</em></strong><em>: We could have created another file called </em><em>config.ts and moved both our </em><em>EnvironmentSchema and </em><em>conf object there, but since we are only using it in the </em><em>index.ts file, I prefer to keep it there. However, if you receive that configuration somewhere else, you should create a </em><em>config.ts file and move it there.</em></blockquote><p>Back to our restAPI.ts file, let’s implement the routes. Most of them will only have path parameters, so I’ll just put them here and explain the logic:</p><pre>import { Hono } from &#39;hono&#39;<br>import { TodoItemService } from &#39;../services/TodoItemService.js&#39;</pre><p>For the post and put routes, we will also need to validate the data coming in, Hono has a validator for Zod that we can use as a middleware, let’s install it with npm i @hono/zod-validator.</p><p>We can import zValidator from it and add it after our route name, but before our handler, like this:</p><pre>app.post(&#39;/api/todos&#39;, zValidator(&#39;json&#39;, ourSchema) async (c) =&gt; {})</pre><p>But we’ll need a few things here, first, we’ll need a new type to represent the creation type, and another one to represent the update type. Let’s add the following code to our TodoItem.ts file:</p><pre>export const TodoObjectCreationSchema = TodoObjectSchema.omit({ id: true, createdAt: true, completed: true }).extend({<br>  dueDate: z.string().datetime()<br>})<br>export const TodoObjectUpdateSchema = TodoObjectSchema.omit({ createdAt: true }).partial().extend({<br>  dueDate: z.string().datetime().optional()<br>})<br><br>export type TodoObjectType = z.infer&lt;typeof TodoObjectSchema&gt;<br>export type TodoObjectCreationType = z.infer&lt;typeof TodoObjectCreationSchema&gt;<br>export type TodoObjectUpdateType = z.infer&lt;typeof TodoObjectUpdateSchema&gt;</pre><p>We are creating new narrower types from a wider type generated by Zod. We need to update this type in the service file:</p><pre>async create(todoItem: TodoObjectCreationType) {<br>  const todo = new TodoItem(todoItem.title, new Date(todoItem.dueDate))<br>  return this.#client.upsert(todo)<br>}<br><br>async update(todoItem: TodoObjectUpdateType) {<br>  const todo = await this.#client.findOne(todoItem.id ?? &#39;&#39;)<br>  if (!todo) {<br>    throw new Error(&#39;Todo not found&#39;)<br>  }<br>  todo.completed = todoItem.completed ?? todo.completed<br>  todo.dueDate = new Date(todoItem.dueDate ?? todo.dueDate)<br>  todo.title = todoItem.title ?? todo.title<br>  return this.#client.upsert(todo)<br>}</pre><p>Now we can use those types in the presentation layer. Let’s add the following code to our restAPI.ts file:</p><pre>app.post(&#39;/api/todos&#39;, zValidator(&#39;json&#39;, TodoObjectCreationSchema), async (c) =&gt; {<br>  try {<br>    const todoItemObject = await c.req.json&lt;TodoObjectCreationType&gt;()<br>    const todo = await service.create(todoItemObject)<br>    c.status(201)<br>    c.json(todo.toObject())<br>  } catch (err) {<br>    c.status(500)<br>    c.json({ error: err })<br>  }<br>})</pre><p>Now for the update route:</p><pre>app.put(&#39;/todos/:id&#39;, zValidator(&#39;json&#39;, TodoObjectUpdateSchema), async (c) =&gt; {<br>  try {<br>    const todoItemObject = await c.req.json&lt;TodoObjectUpdateType&gt;()<br>    const todo = await service.update({ ...todoItemObject, id: c.req.param(&#39;id&#39;) })<br>    c.status(200)<br>    return c.json(todo.toObject())<br>  } catch (err) {<br>    c.status(500)<br>    return c.json({ error: err })<br>  }<br>})</pre><p>And that’s it! We have our presentation layer ready to go!</p><h3>Testing</h3><p>Testing our application is a matter of running npm run start:dev and making requests to it. To test it better I’ll leave a <a href="https://hurl.dev/">Hurl</a> file in the repository that you can use to test the API. You can run it using hurl --test ./collection.hurl. This will run all the tests and make sure that everything is working as expected.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/678/1*EQ-UHFN6-1aYKg9A0m_QNw.png" /></figure><p>You can also make requests yourself to the API using curl or any other tool you prefer. Then check your HarperDB studio to see if the data was correctly inserted.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/1*8tcVxL3AJfrF6s-xAAauBA.png" /></figure><h3>Conclusion</h3><p>In this article, we learned how to set up a TypeScript project with HarperDB, we learned how to separate our code into layers, and we learned how to test our application. We also learned some best practices along the way.</p><p>The idea is that those layers are not set in stone, you can add more layers if you need to, or remove some layers if you don’t need them. The important thing is to have a clear separation of concerns and to make sure that your code is maintainable.</p><h3>Stackademic</h3><p><em>Thank you for reading until the end. Before you go:</em></p><ul><li><em>Please consider </em><strong><em>clapping</em></strong><em> and </em><strong><em>following</em></strong><em> the writer! 👏</em></li><li><em>Follow us on </em><a href="https://twitter.com/stackademichq"><strong><em>Twitter(X)</em></strong></a><em>, </em><a href="https://www.linkedin.com/company/stackademic"><strong><em>LinkedIn</em></strong></a><em>, and </em><a href="https://www.youtube.com/c/stackademic"><strong><em>YouTube</em></strong></a><strong><em>.</em></strong></li><li><em>Visit </em><a href="http://stackademic.com/"><strong><em>Stackademic.com</em></strong></a><em> to find out more about how we are democratizing free programming education around the world.</em></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=758f63d84885" width="1" height="1" alt=""><hr><p><a href="https://blog.stackademic.com/best-practices-for-harperdb-projects-using-typescript-758f63d84885">Best practices for HarperDB projects using TypeScript</a> was originally published in <a href="https://blog.stackademic.com">Stackademic</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Simple Multi-Node Data Replication with HarperDB Pub/Sub]]></title>
            <link>https://faun.pub/simple-multi-node-data-replication-with-harperdb-pub-sub-1ed45cdedc30?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/1ed45cdedc30</guid>
            <category><![CDATA[clustering]]></category>
            <category><![CDATA[harperdb]]></category>
            <category><![CDATA[pub-sub]]></category>
            <category><![CDATA[database]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Sat, 22 Oct 2022 11:31:33 GMT</pubDate>
            <atom:updated>2022-10-22T11:31:33.027Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wb2TCcopnegExIKXR1c0vQ.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@trommelkopf?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Steve Harvey</a> on <a href="https://unsplash.com/s/photos/broadcast?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>In the previous articles that I’ve written about HarperDB, we’ve explored the realms of <a href="https://medium.com/@khaosdoctor/using-harperdb-with-kubernetes-e796ea606e99">Kubernetes</a>, and <a href="https://faun.pub/running-harperdb-in-kubernetes-in-one-command-8c87e2788eb6">Helm</a>. Now it’s time to step back a little and try to understand another cool feature that it offers in this short and straight-to-the-point article.</p><p>Let’s imagine we work for a company that provides business intelligence on productive processes for big factories across the world and you’re launching a new product; Intelligent factories! To do that, the factories would be equipped with the most varied types of sensors that monitor several of the machines’ key properties, like pressure, temperature, and weight put on it.</p><p>The problem is that all this data needs to be sent back to the company’s main servers so we can apply our machine learning models and big data analysis on top of them. And they’ve asked you to provide a nice architecture to make it happen. The process has a few rules:</p><ul><li>The sensors send data at a very fast rate, so sending them as you receive would not be wise as it would clutter the network, making data streaming a sub optimal solution.</li><li>The data should be stored in a database at the company in raw format.</li><li>The sensors will keep sending data even in case of a network failure. You need to sync that data with the company’s servers as soon as the network returns.</li><li>The company must be able to send configurations to the sensors installed in the customers from the HQ, without the need for a local visit.</li></ul><p>How can you perform all these tasks with the minimum amount of effort and using the best technology? Enters <strong>HarperDB clusters</strong>.</p><h3>Clusters</h3><p>When we say clusters, the base idea that comes to us is something like Kubernetes, right? A bunch of virtual machines that behave as a single, big one. However, HarperDB takes this idea a step further. Since we’re talking about a database, the concept of “work as one” boils down to having the same data in all instances eventually, and this is, in fact, the concept of a cluster of databases.</p><p>However, in other DBs, all the data will be replicated across the whole cluster no matter what, they’ll be identical eventually, then why can’t you choose which data you want to sync?</p><p>When you cluster two or more instances of a HarperDB database together, they’ll ask you which data you wish to share, this is called a <strong>subscription</strong>. So, basically, what HarperDB has is a very robust and fancy implementation of a publish/subscribe model that works at the table level and has a fault-tolerant mechanism that allows it to lose the connection at any given time and automatically re-synchronize the data once it’s back up.</p><h3>How do clusters work?</h3><p>I won’t extend myself in this part as HarperDB has a <a href="https://harperdb.io/docs/clustering/">very nice documentation about it</a>, but the idea is that each database composes a <strong>node</strong> in the cluster, and the whole group of nodes in a cluster and their configurations is called a <strong>topology</strong>.</p><p>To send and receive data, the nodes rely on <strong>channels</strong>, which are named as schema: table, each channel represents a connection between two nodes, sending or receiving data from a specific table. Channels have two options, they can either be <strong>publishers</strong>, <strong>subscribers</strong>, or both.</p><p>A <strong>publishing</strong> channel will post all the changes of that specific table to any other nodes that are <strong>subscribing to it</strong>, let’s see an example. If I wanted to publish all the changes from the schema named <em>dev</em> on the table <em>dog</em> from the first node to the second one, I could create a <strong>publish channel</strong> called dev:dog.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/844/0*FBlAAS6fIdxr7AoX" /></figure><p>On the other hand, I could express it differently, I could say that I want to listen to all the changes in the <em>dog</em> table in node 1, so I can <strong>subscribe to it</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/844/0*2LAf8G8hdH1KySxD" /></figure><p>When you create a subscription in any node, any changes to that particular table will be synced to the other tables, any new data on dev:dogwill be published to the other node, as well as any updates or removals. If the table doesn’t exist in one of the nodes, it’ll also be created, however, <strong>destructive operations like drop won’t be propagated.</strong></p><h3>Creating a cluster</h3><p>So, you decide to create a proof of concept to see if your idea to use HarperDB’s clusters will work. Since HarperDB has a Docker image with clustering available, we’ll start by creating a Docker Compose file.</p><p>The idea is to simulate two instances of a HarperDB node and connect them both into a cluster. For this we’ll create two services in your Docker Compose, the first will be the one representing the sensor:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bc063c4393b77664759c63aee5d1b84f/href">https://medium.com/media/bc063c4393b77664759c63aee5d1b84f/href</a></iframe><p>We’ll open ports <strong>9925</strong>, <strong>9926</strong> and <strong>62344</strong>, but since we’ll spin up another service in the same computer, we cannot just use the same ports, so we’ll map them to <strong>9900</strong>, <strong>9901</strong>, and <strong>6200</strong> respectively. We’ll enable clustering in the environment variables, then we’ll set the cluster port to <strong>62344</strong> as we set in the port manifest. Lastly, we’ll name the node as <em>harper-edge</em>.</p><p>For the second instance that will represent the company’s server, we’ll copy the first part and just change the names and ports, the final file will be like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ffc1ad7a508cd5fadb38bf0be715c871/href">https://medium.com/media/ffc1ad7a508cd5fadb38bf0be715c871/href</a></iframe><p>We are just summing 1 in the ports so they don’t conflict, and also changing the name of the node. The important part here is that the cluster user and password <strong>must be the same in both services</strong>.</p><p>Let’s spin that up with docker compose up, you should see the result of two HarperDB instances being run. Now let’s go to the <a href="https://studio.harperdb.io/">HarperDB Studio</a> and register them. Just create your account, organization, and click on register a new user-managed instance. This should be the data we have:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/974/0*rPZUm03zzBjudJZ3" /></figure><p>Select the free instance and accept the terms, we should be ready to go:</p><p>Let’s do the same for the other one:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/702/0*mF4H4rYoq8al3H1E" /></figure><p>What will change will only be the port number, which is now 9902.</p><h3>Connecting the nodes</h3><p>We won’t create any tables on any of the instances. Instead, let’s jump into our studio and go to the “Cluster” tab on any of those instances:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/722/0*-aJYOw_xv-SqK2GN" /></figure><p>As you can see, we have our other node listed, but when we try to connect we get an error:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/972/0*VTn4NFC6A46tk6t2" /></figure><p>Since we’re using Docker, the instance is not available through the HarperDB studio to be connected to the other node in the cluster, in this case, we’ll need to take the matters into our own hands, literally.</p><p>HarperDB has an API that allows you to manually create a connection between two nodes. On any request tool you have — like Postman, Insomnia, or any other — use the URL: <a href="http://localhost:9902">http://localhost:9902</a> in a POST request. You can use any of the hosts to do what we’re about to do, but to keep the idea of the project, let’s connect to the host one representing the company’s database.</p><p>In the auth section, we’ll use a basic HTTP auth, the username and password are the ones we defined in the compose file, in this case both of them are admin. Now we’ll create a JSON body that looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/955364b6c84cd9ee9d1bf69920723a09/href">https://medium.com/media/955364b6c84cd9ee9d1bf69920723a09/href</a></iframe><p>So, from the company’s database, we want to <strong>publish</strong> configurations and <strong>subscribe</strong> to sensor data, let’s tell the node that with two objects inside the subscriptions key:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/47173441530bb010f38c115e3a14ea08/href">https://medium.com/media/47173441530bb010f38c115e3a14ea08/href</a></iframe><p>Now, we go back to our studio and log on to the <strong>harper-host</strong> instance. We’ll create our schema called default and a table called configs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/726/0*SYnYcfccQhcIp3js" /></figure><p>Now, let’s add a simple data to that table by clicking the + button on the top right:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/672/0*Mw8gaFEkAUIEsofE" /></figure><p>After saving, let’s switch to the harper-edge instance, and look! We now have a configs table and inside that table we have the same item we just added:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*DywexoDnPd3FVlP6" /></figure><p>Let’s do the same on the harper-edge instance, and add a table called sensor_data with a hash of id, then add some data on it:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/490/0*8Il6d7IrwTHcbZeq" /></figure><p>And if we switch back to the host, we’ll also have the same data over there:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*kC_rwv_EQDeGNspP" /></figure><p>This is valid even if we delete the data on one of the sides, in this case the data on the other end will also be deleted.</p><h3>Conclusion</h3><p>This was the first glimpse of this amazing feature. Stay tuned for the next tutorials on how we can use Harper’s pub/sub to make it work in our favor in other real-world examples.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*nTpwFrWVp057bnqw.png" /></figure><p>If you find this helpful, please click the clap 👏 button below a few times to show your support for the author 👇</p><h3>🚀<a href="http://from.faun.to/r/8zxxd">Join FAUN &amp; get similar stories in your inbox each week</a></h3><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1ed45cdedc30" width="1" height="1" alt=""><hr><p><a href="https://faun.pub/simple-multi-node-data-replication-with-harperdb-pub-sub-1ed45cdedc30">Simple Multi-Node Data Replication with HarperDB Pub/Sub</a> was originally published in <a href="https://faun.pub">FAUN.dev() 🐾</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Running HarperDB in Kubernetes in one command]]></title>
            <link>https://faun.pub/running-harperdb-in-kubernetes-in-one-command-8c87e2788eb6?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/8c87e2788eb6</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[helm]]></category>
            <category><![CDATA[harperdb]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Wed, 17 Aug 2022 17:49:36 GMT</pubDate>
            <atom:updated>2022-08-17T23:27:50.155Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wTIOXUBnJlijJgbr-m16Og.png" /></figure><p>A while ago, I wrote <a href="https://medium.com/@khaosdoctor/using-harperdb-with-kubernetes-e796ea606e99">this article</a>, which explained how we could run a <a href="https://harperdb.io/">HarperDB</a> instance using a Kubernetes cluster hosted on Azure!</p><p>As you can see, this article is pretty long, it has a series of commands and a series of manifest files that we create and need to keep updated in order to make the database work properly in the cluster.</p><p>What if I told you we could do this using a single command?</p><h3>Helm</h3><p>Kubernetes alone is a great tool; however, no one is the greatest alone, and this is why Kubernetes has a younger brother called Helm.</p><p>Helm is one of those tools created with the purpose of filling a small gap in an existent technology, sometimes this gap is so huge that it becomes a very respectable and useful tool, so much that is even quite difficult to use the original tool without it.</p><p>I explained <strong>a lot</strong> about Helm in <a href="https://www.freecodecamp.org/news/what-is-a-helm-chart-tutorial-for-kubernetes-beginners/">this article on FreeCodeCamp</a> some time ago, so I won&#39;t be explaining it all over again. Instead, I&#39;ll quickly skim through what you need to know the most about it.</p><p>The purpose that Helm serves is basically to <strong>pack manifest collections into Charts</strong>. Charts are like application libraries — yes, just like NPM, NuGet, pip, gems and so on — the only difference is that it doesn&#39;t contain actual code, but a series of go-like templates that will eventually be parsed into useful YAML files, which are the Kubernetes manifests.</p><p>This way we can group all the manifests from a single application into one Chart and install it in a single command. But, beyond that, what is even more useful than being able to install entire apps in a whim is the possibility to <strong>compile templates just-in-time</strong>, which means we can have variable data inside those templates.</p><p>For example, in the other article, we had this code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ae83b8ba0728c23881ed16787ab7794/href">https://medium.com/media/4ae83b8ba0728c23881ed16787ab7794/href</a></iframe><p>This is a Kubernetes deployment for <a href="http://harperdb.io">HarperDB</a>, but it contains some environment variables and this data is often changed, so we could do this instead:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a354940c3749fafa41db4caed6eeb8dc/href">https://medium.com/media/a354940c3749fafa41db4caed6eeb8dc/href</a></iframe><p>See the {{ .Values.HDB_USERNAME }} , well, that&#39;s a template variable. And we can change it in runtime.</p><h3>Preparing the ground</h3><blockquote>Below I’ll be using Azure, but you can use whatever cloud provider you want. Which means that, if you’re not using Azure, then you probably don’t need the Azure CLI.</blockquote><p>Before we start, head over to the <a href="https://helm.sh/docs/intro/install/">Helm</a> website and install the Helm binary, as well as the <a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli">Azure CLI</a> and the <a href="https://kubernetes.io/docs/tasks/tools/">Kubectl</a> binaries.</p><p>If everything went correctly, you should have three new commands in your terminal: kubectl , az , and helm .</p><p>First, use az login to log in to your Azure account. Then, you should create a resource group with the command az group create -n harperdb-helm after that let&#39;s create our cluster with the following command:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/15533d2fb557c3bb97d244b1cfefa3ac/href">https://medium.com/media/15533d2fb557c3bb97d244b1cfefa3ac/href</a></iframe><p>The whole process takes about a minute or two, so be patient. After it&#39;s done, you can run az aks get-credentials -n harperdb-helm -g harperdb-helm --admin and this should populate your kubeconfig file with the credentials to the cluster.</p><p>If everything is fine, then when you issue kubectl get nodes you should see a list with one node.</p><h3>Helm charts</h3><p>As I mentioned in the beginning, Helm groups the packages into charts. Charts have a very specific directory structure.</p><p>To create a chart we simply type helm create harperdb and this will give us another directory with the following structure:</p><blockquote>└── harperdb<br> ├── Chart.yaml<br> ├── charts<br> ├── templates<br> │ ├── NOTES.txt<br> │ ├── _helpers.tpl<br> │ ├── deployment.yaml<br> │ ├── hpa.yaml<br> │ ├── ingress.yaml<br> │ ├── service.yaml<br> │ ├── serviceaccount.yaml<br> │ └── tests<br> │ └── test-connection.yaml<br> └── values.yaml</blockquote><p>You can find a detailed explanation on the directory structure on <a href="https://www.freecodecamp.org/news/what-is-a-helm-chart-tutorial-for-kubernetes-beginners/">my article</a>, but what we need to know is that we can delete all the files inside the templates directory, we can also delete the charts directory. The final directory structure will be:</p><blockquote>└── harperdb<br> ├── Chart.yaml<br> ├── templates<br> └── values.yaml</blockquote><p>The Chart.yaml file is the one that names our package, you can open and edit it to be like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f9291dd73ec55d1cac0c9b1e5cd2daad/href">https://medium.com/media/f9291dd73ec55d1cac0c9b1e5cd2daad/href</a></iframe><h4>Template files</h4><p>Then we can start by creating our template files. It&#39;s as simple as copying and pasting our previous templates from <a href="https://medium.com/@khaosdoctor/using-harperdb-with-kubernetes-e796ea606e99">this article</a> into their own files. Let&#39;s start by the deployment. Create a new deployment.yaml file inside templates with this content:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ae83b8ba0728c23881ed16787ab7794/href">https://medium.com/media/4ae83b8ba0728c23881ed16787ab7794/href</a></iframe><p>Now we&#39;ll apply the template variables into this document, this will be the final result:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ea09b55220ce4658c58ceb7f17fbad5/href">https://medium.com/media/4ea09b55220ce4658c58ceb7f17fbad5/href</a></iframe><p>Notice that we&#39;re using some functions like default , required , quote and the special namespace .Values . Golang templates (the type that helm uses) have several functions to help formatting data when used.</p><p>The default and required functions, as you may expect, define values that are default, in this case, we&#39;re getting the namespace of the release unless we say explicitly that we want it to be somewhere else. Same goes for required , it&#39;ll pop an error if it fails to receive the data.</p><p>The .Values namespace refers to our values.yaml file, it&#39;s where we&#39;ll define the values for those variables.</p><p>Next, create a new file called persistentVolumeClaim.yaml with this content:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2f976121211ad044cdbf23fbb91e6c50/href">https://medium.com/media/2f976121211ad044cdbf23fbb91e6c50/href</a></iframe><p>And we&#39;ll do the same thing, replace the variable values for placeholders:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/876a7cf1bc92b7d04de01d2d02188852/href">https://medium.com/media/876a7cf1bc92b7d04de01d2d02188852/href</a></iframe><p>Now we&#39;ll jump to the service, create a service.yaml file with these contents:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d91bdd79b52e1ced2c48461e798d24c2/href">https://medium.com/media/d91bdd79b52e1ced2c48461e798d24c2/href</a></iframe><p>And this will be the version with the placeholders:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cf73e81824448e63698710df0eb46940/href">https://medium.com/media/cf73e81824448e63698710df0eb46940/href</a></iframe><h4>Values</h4><p>This would be all files that we need for the templating part. Now let&#39;s create our values file. As you can see, the values.yaml file already has some content, you can delete it all and we&#39;ll create all the variables that we&#39;re using in the templates. The final file will look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/82c4a4f4ef0f8e33f6a29c6a40cc14ac/href">https://medium.com/media/82c4a4f4ef0f8e33f6a29c6a40cc14ac/href</a></iframe><p>The values we put in the file will be the default values of the placeholders they match, for example, the .Values.name will match the name: harperdb value of the YAML, which means this has a default value.</p><p>Also pay attention that we can scope variables inside other objects, like we&#39;re doing for service.port and env. Also, about the env those are environment variables, which don&#39;t contain a default value.</p><h3>Install the chart</h3><p>To deploy our application, we need to install the chart to the cluster. But before, let&#39;s check if everything is correct. To do that we&#39;ll render the template in our terminal using the following command:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/169c763d1014879bc8c0d4de2d4debaa/href">https://medium.com/media/169c763d1014879bc8c0d4de2d4debaa/href</a></iframe><p>The -n flag tells us the namespace we want to install, while the --set flags will update the values inside the values.yaml file, and consequently, our manifest files as well.</p><p>If all went well you should have this output:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a610672e318408efd6102f5289e17e37/href">https://medium.com/media/a610672e318408efd6102f5289e17e37/href</a></iframe><p>Now that everything is fine, we can install our chart with the following command:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1f01fbdd17a6c0ec00f14f361bab18a2/href">https://medium.com/media/1f01fbdd17a6c0ec00f14f361bab18a2/href</a></iframe><p>We&#39;re creating a release named harperdb out of a local chart (the last argument), you can name your release whatever you want and also host your chart (but this will be a matter for other tutorials).</p><p>You can check the deployment using the command helm list -n database. To remove the release just type helm delete harperdb -n database.</p><p>To connect to the database, you may refer to the <a href="https://medium.com/@khaosdoctor/using-harperdb-with-kubernetes-e796ea606e99?source=read_next_recirc---------1---------------------50244d20_68e0_42bb_8fae_92f7039601ea-------">previous article</a> and jump to the &quot;Accessing HarperDB&quot; session.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*7ezJJxYOY_paiVI4.png" /></figure><h4>If this post was helpful, please click the clap 👏 button below a few times to show your support for the author 👇</h4><h4>🚀Developers: Learn and grow by keeping up with what matters, <a href="https://faun.to/8zxxd">JOIN FAUN.</a></h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8c87e2788eb6" width="1" height="1" alt=""><hr><p><a href="https://faun.pub/running-harperdb-in-kubernetes-in-one-command-8c87e2788eb6">Running HarperDB in Kubernetes in one command</a> was originally published in <a href="https://faun.pub">FAUN.dev() 🐾</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using HarperDB with Kubernetes]]></title>
            <link>https://faun.pub/using-harperdb-with-kubernetes-e796ea606e99?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/e796ea606e99</guid>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[harperdb]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Thu, 30 Jun 2022 13:36:07 GMT</pubDate>
            <atom:updated>2022-10-03T23:13:40.263Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*I_e4KKAUBDRzDATM" /><figcaption>Photo by <a href="https://unsplash.com/@sandrokatalina?utm_source=medium&amp;utm_medium=referral">Sandro Katalina</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>When working with any sort of application, one of the biggest challenges is to make it reusable and maintainable. This is why we usually choose containers as the tool to get around this problem.</p><p>Containers are simple to build and easy to maintain. A while ago I <a href="https://www.freecodecamp.org/news/what-is-docker-used-for-a-docker-container-tutorial-for-beginners/">wrote this article on FreeCodeCamp</a> that explains a lot about what containers are and why we should use them.</p><p>Now, while building an application recently, I ran into another problem: <strong>How can I run my database, using HarperDB, in a container? And then how can I install it using Kubernetes?</strong></p><p>After some brainstorming I came up with a solution, and now it’s the time to share that with you!</p><p>But, first things first, what is <a href="https://harperdb.io/">HarperDB</a>?</p><h3>HarperDB</h3><p><a href="https://harperdb.io/">HarperDB</a> is a multi-paradigm, general-purpose database that is designed to be fast and easy to use. It encapsulates SQL and NoSQL functionality, CSV data aggregation and a whole lot of helpers like Math.js for working with math, GeoJSON support for working with geographical data, and moment for working with dates.</p><p>Ok, but what’s the big catch? Other databases already do this. The catch is that HarperDB stores this as a unified data structure and serves it through a unique storage algorithm that makes it really fast for all the different types of data.</p><p>On top of that, HarperDB is also projected to be a full-fledged solution to build not only your database alone, but also a complete API that interacts with that through the use of <a href="https://harperdb.io/docs/custom-functions/">Custom Functions</a> (which I’ll talk about in another article), websockets, and the cherry on top is that it doesn’t require a specific driver to work with. It exposes a standard REST API that can be used by any language, so you can both query data and perform operations using simple HTTP calls.</p><p>Here’s a quick diagram of how HarperDB works, taken straight from the <a href="https://harperdb.io/product">HarperDB website</a>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/776/1*ODcNtDsLR6xs24_2Piim1w.jpeg" /></figure><p>In short, I wanted to try it out and wasn’t disappointed! Now, how can we install this database in a Kubernetes environment?</p><h3>Kubernetes and Databases</h3><p>Usually, when dealing with databases, we tend to install it on a separate computer or VM, and connect our application to that. While this is a good option to make it easier to manage, it’s not the best option to make it faster, nor distributable, highly available, or reliable.</p><p>To do that, we need to distribute the database into several machines using a cluster approach, which means we have a main control plane, which is the main connection point for a database, and several other instances of the same database on which the control plane will distribute the load. These instances are called nodes.</p><p>Some nodes can be read only, some can be write only, some can be both. This allows us to make sure we have the correct permissions for each node, and we can also make sure that we don’t have any data loss or corruption. When any data is inserted, deleted, or changed in the database, these nodes publish a message to other nodes to make that change too, so that the data is always consistent. However, it takes a while for this data to be replicated when we have a large infrastructure, this is what we call <a href="https://en.wikipedia.org/wiki/Eventual_consistency">eventual consistency</a>.</p><p>In this article I’ll be creating a single HarperDB instance on Kubernetes, however, I plan to show you how to create a cluster of HarperDB instances in future articles.</p><h3>Containerizing HarperDB</h3><p>Most databases already have a container-ready approach, and Harper is not different. You can find the containerized version of HarperDB in <a href="https://hub.docker.com/r/harperdb/harperdb">their Docker Hub page</a>, it’s very straight-forward to install and run. Before putting the database in Kubernetes, let’s make sure we can run it locally using Docker.</p><blockquote>If you don’t have Docker installed, you can install it from the <a href="https://www.docker.com/get-started/">Docker website</a>.</blockquote><p>The simple and easy command to run is:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5c35f983ac280d56a59ee39aeb99ed74/href">https://medium.com/media/5c35f983ac280d56a59ee39aeb99ed74/href</a></iframe><p>HarperDB needs port 9925 to be opened so we can connect it to the <a href="https://studio.harperdb.io/">HarperDB Studio</a> application, so we can manage it straight from the browser.</p><p>Aside from that, we need to remember that containers are <em>ephemeral</em>, which means they <strong>do not</strong> persist after they are stopped. This is why we need a volume to persist the data, for that we can add the -v flag in our Docker command, pointing to the /opt/harperdb/hdb directory inside the container:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/328bd0de150eb26fffc657f5be819718/href">https://medium.com/media/328bd0de150eb26fffc657f5be819718/href</a></iframe><p>In this command we’re telling Docker to mount the /opt/harperdb/hdb directory in a local folder called /.hdb, so that we can persist the data. After the command is run, we can ls -la the /.hdb directory to see that it’s actually mounted:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/471/1*lOHjHeV_U5k9oEOUD8DKig.png" /></figure><p>Now, we go to the <a href="https://studio.harperdb.io/">HarperDB Studio</a> application and login. If you don’t have an account yet, go to the <a href="https://studio.harperdb.io/sign-up">sign-up page</a> and create one, it’s free. Then create an organization for you and you should land on this page:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1009/1*2soch02JkPrlpE9Z3zBFSQ.png" /></figure><p>Let’s create a local instance by clicking the <strong>Register User-installed Instance</strong> button:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/938/1*cXe0Seiglzu0Ul76zTZI7g.png" /></figure><p>Let’s fill up the details of the instance, and then click the <strong>instance details</strong> button:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/555/1*pIoByrjcrTe97lMnRTgNIw.png" /></figure><p>Then we select the free tier and click next:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/514/1*iQT5JBYFhq4iV3-uIySfjg.png" /></figure><p>Confirm the terms of service and <strong>Add Instance</strong>. You should end up with a local registered instance of the database:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/976/1*23BlspfXgPuDiboSQjVyQw.png" /></figure><p>If you click it, you should have access to all your schemas and tables. So that’s what we need to do.</p><h3>Migrating to Kubernetes</h3><p>First, let’s set up a plan of what we need to do:</p><p><strong>1.</strong> Create a Kubernetes cluster<br><strong>2. </strong>Create a namespace called <strong>database</strong> so we can organize our resources<br><strong>3. </strong>Create a deployment for HarperDB so we can deploy it to the cluster<br><strong>4.</strong> Create a persistent volume claim for the /opt/harperdb/hdb directory<br><strong>5.</strong> Create a service for HarperDB so we can port-forward it locally and connect to it from the browser</p><p>Since we’ll not be exposing the database to the internet, we won’t create an Ingress rule for it, instead, we’ll only connect to it from inside the cluster.</p><h4>Create a Kubernetes cluster</h4><p>There are a ton of ways to create a new Kubernetes cluster, but the easiest and fastest way is to use a managed cluster. In this tutorial we’ll be using Azure’s AKS, but you can use any other provider.</p><p>The first thing is to log in to your Azure account on the <a href="https://portal.azure.com/">Azure Portal</a>, if you don’t have an Azure account, just create one in the <a href="https://azure.microsoft.com">Azure website</a>. There are two ways to create a managed AKS cluster, one of them is through the <a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli">Azure CLI</a>, the other is through the portal itself, which is the way we’ll be doing this tutorial.</p><p>Search for “Kubernetes” in the Azure Portal, and click on the <strong>Kubernetes Services</strong> result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/577/1*MZKmsvYZ1dIYI6g8i9FvRw.png" /></figure><p>Then click on <strong>create</strong> dropdown and create a new cluster:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/273/1*_Y_SBriC6812LzRd0-UxFQ.png" /></figure><p>Let’s create a new resource group called harperdb-aks, let’s change the preset to development to save some money since we’ll not be using the cluster for production, then name the cluster harperdb, choose your preferred region:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/772/1*6ve76OzS2COpsuKFOCkBEA.png" /></figure><p>Let’s select the <strong>B2s</strong> instance in the instance picker and remove the autoscale method, since we’ll be using a fixed size cluster of 1 single node:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/790/1*Wqgx8nivj5gsG6sRnbQhGA.png" /></figure><p>Click the blue <strong>Review+Create</strong> button to create the cluster:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/717/1*PNczk9nKE_eznGIoQl9Cyw.png" /></figure><p>The process will take a while to complete, so let’s wait until it’s done.</p><blockquote>While you wait, take your time to install and login to the <a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli">Azure CLI</a> if you haven’t done so yet, because we’ll need it to connect to the cluster.</blockquote><p>After the process is complete, jump to your terminal, start your <strong>az cli</strong> and install the <strong>kubectl</strong> tool with <strong>az aks install-cli</strong>. After that we’ll download the credentials to our <strong>kubeconfig</strong> file, so we can connect to the cluster using <strong>az aks get-credentials -n harperdb -g harperdb-aks — admin</strong>.</p><p>If everything went well, you should be able to connect to the cluster with <strong>kubectl get nodes</strong> and get a response with a single node like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bd787d9350116eb7baf1016fafb4e1f2/href">https://medium.com/media/bd787d9350116eb7baf1016fafb4e1f2/href</a></iframe><p>Let’s create our namespace with <strong>kubectl create namespace database</strong>, and now we’re able to create our manifests.</p><h3>Create manifests</h3><p>To start the manifest creation, we’ll need to create a deployment for HarperDB, so we can deploy it to the cluster. Let’s create a new file called <strong>manifests.yaml</strong> where we’ll add all the manifests we’ll need. In this file, we’ll start with the deployment:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ae83b8ba0728c23881ed16787ab7794/href">https://medium.com/media/4ae83b8ba0728c23881ed16787ab7794/href</a></iframe><p>Before we go further, some explanation on volumes is necessary. Since we’re dealing with managed volumes on Azure, we don’t need (and can’t) create a volume inside the host nor inside the pod because they’ll be deleted when we delete the resources. What we want to do is to delegate the creation process to Azure through <strong>StorageClasses</strong> and <strong>PersistentVolumeClaims</strong>.</p><blockquote>The usual process is to create a <strong>PersistentVolume</strong> and then claim it with a <strong>PersistentVolumeClaim</strong></blockquote><p>The idea is that we declare that we want to claim a determined amount of space from the Azure pool for the volume, and then we’ll mount it to the path we want. Azure will receive this request through a Kubernetes operator that manages the CSI (Container Storage Interface) plugin and create the volume in our Azure account.</p><p>By default, Azure has two types of storage: Azure File and Azure Disk. We’ll be using the Azure File storage because it’s easier to see the files in the Azure Portal, also, clusterization is only supported in Azure File storage.</p><p>Also note that we’re mounting the volume not on <strong>/opt/harperdb/hdb</strong> but on <strong>/opt/harperdb</strong>. This is because if we mount the volume in the innermost directory, we’ll have permission problems since the outermost directory is owned by the user that runs the container, and the innermost directory is created by Azure.</p><p>The other important information is that HDB takes a small while to start, so we’re setting a <strong>livenessProbe</strong> and <strong>readinessProbe</strong> to check if the pod is ready, these probes will check the connection on TCP port 9925, when this port is open, the service is online.</p><p>Next, we’ll add the persistent volume claim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2f976121211ad044cdbf23fbb91e6c50/href">https://medium.com/media/2f976121211ad044cdbf23fbb91e6c50/href</a></iframe><p>And to finish the manifest file, we’ll add a service:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d91bdd79b52e1ced2c48461e798d24c2/href">https://medium.com/media/d91bdd79b52e1ced2c48461e798d24c2/href</a></iframe><p>The final file will look like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/aa576baad71a119c600dfd248a6a90c6/href">https://medium.com/media/aa576baad71a119c600dfd248a6a90c6/href</a></iframe><p>The last thing is to apply the manifests to the cluster using <strong>kubectl apply -f ./manifests.yml</strong>. You’ll receive a message like this:</p><blockquote>persistentvolumeclaim/harperdb-data created<br>deployment.apps/harperdb created<br>service/harperdb created</blockquote><p>We can check if everything is fine by running <strong>kubectl get deploy harperdb -n database</strong>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2a46b8d2330354ebef2fb5a69f3ace58/href">https://medium.com/media/2a46b8d2330354ebef2fb5a69f3ace58/href</a></iframe><p>Then we can take a look on our Azure Portal and see if the volume is there. First, select <strong>Resource Groups</strong> in the portal, you can do this by searching for it or clicking on the home page.</p><p>Find a resource group that matches <strong>MC_harperdb-aks_harperdb_&lt;location&gt;</strong>, click it, you should see a bunch of resources.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/808/1*9XoBuQ_7FEH7UDkN8VIiNA.png" /></figure><p>One of these resources is our Storage Account, click it. Then, in the left panel, go to <strong>File Shares</strong>, you should see an Azure File volume starting with <strong>pvc-</strong>, click it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/985/1*QioW_Cgj0GKWdDxIG7w2rA.png" /></figure><p>Here’s our mounted volume with all HDB’s files. Now let’s access our HDB instance and check if everything is working.</p><h3>Accessing HarperDB</h3><p>To access the database from the outside world, since we didn’t expose it as a good practice, let’s port forward our own connection to our service connection using the following command:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fdbef671e7002bc20d5abee9e61060a2/href">https://medium.com/media/fdbef671e7002bc20d5abee9e61060a2/href</a></iframe><p>You should see the following messages:</p><blockquote>Forwarding from 127.0.0.1:9925 -&gt; 9925<br>Forwarding from [::1]:9925 -&gt; 9925<br>Handling connection for 9925<br>Handling connection for 9925<br>Handling connection for 9925<br>…</blockquote><p>Then, let’s go to our studio in the browser and create a new instance with the credentials that we included in our deployment manifest, the username and the password are <strong>harperdb</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/516/1*Pw866FCNdIDo2T7Gightww.png" /></figure><p>After that, you can follow the same steps to create a new database, create a new user, and then grant permissions to the user.</p><h3>Conclusion</h3><p>We made it to the end of this tutorial and I hope you enjoyed it! Now you’re able to create your own HDB instance in a Kubernetes cluster.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*VUK1-iKIQ90xUlTO.png" /></figure><p><strong>If you find this helpful, please click the clap 👏 button below a few times to show your support for the author 👇</strong></p><h4>🚀<a href="http://from.faun.to/r/8zxxd">Join FAUN &amp; get similar stories in your inbox each week</a></h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e796ea606e99" width="1" height="1" alt=""><hr><p><a href="https://faun.pub/using-harperdb-with-kubernetes-e796ea606e99">Using HarperDB with Kubernetes</a> was originally published in <a href="https://faun.pub">FAUN.dev() 🐾</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como e Por Que Todo Dev Deveria Escrever Artigos]]></title>
            <link>https://medium.com/@khaosdoctor/como-e-por-que-todo-dev-deveria-escrever-artigos-1f8b9ba74d4?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/1f8b9ba74d4</guid>
            <category><![CDATA[carreira]]></category>
            <category><![CDATA[escrita]]></category>
            <category><![CDATA[articles]]></category>
            <category><![CDATA[desenvolvimento-pessoal]]></category>
            <category><![CDATA[developer]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Thu, 14 May 2020 16:23:24 GMT</pubDate>
            <atom:updated>2020-07-31T17:45:54.491Z</atom:updated>
            <content:encoded><![CDATA[<h3>Por Que Devs Deveriam Escrever Artigos</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*AjQT6QaFF7m5oyhU" /><figcaption>Photo por <a href="https://unsplash.com/@glenncarstenspeters?utm_source=medium&amp;utm_medium=referral">Glenn Carstens-Peters</a> no <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Recentemente estive passando pelos tópicos no <a href="https://dev.to/khaosdoctor">DEV Community</a> e encontrei um <a href="https://dev.to/iriskatastic/writing-technical-articles-tips-for-programmers-29gp">excelente artigo</a> sobre algumas dicas para pessoas que queiram começar a escrever artigos técnicos. Então resolvi escrever um artigo com a minha própria opinião sobre o assunto e também referenciar algumas partes deste conteúdo sensacional.</p><blockquote><em>Algumas das dicas e referências a seguir serão copiadas deste artigo somente para fins práticos, para evitar que o leitor tenha que buscar o conteúdo no artigo original. Porém, as opiniões sobre ele são próprias.</em></blockquote><h3>Por que escrever?</h3><p>Todas as pessoas, sendo programadoras ou não, deveriam escrever sobre o que fazem e sobre o que estão aprendendo, ou então escrever somente sobre a vida que estão levando.</p><p>Um ponto interessante que é colocado no artigo é algo que não pensamos muito. Sempre que lemos um conteúdo técnico na Internet, associamos o criador daquele conteúdo como sendo uma autoridade ou pelo menos tendo certa autoridade no assunto, e isso é algo extremamente positivo para a sua carreira e também para você como pessoa.</p><blockquote><em>Ser associado com experts no assunto é sempre uma boa prática. Quando as pessoas leem seu artigo na Internet, elas te reconhecem como um profissional e confiam mais no que você diz.</em></blockquote><blockquote><em>- Extraído da </em><a href="https://dev.to/iriskatastic/writing-technical-articles-tips-for-programmers-29gp"><em>referência</em></a></blockquote><p>E este é, entre outros, um dos pontos por que você, como pessoa desenvolvedora, deveria começar a escrever conteúdo técnico, a consolidação do seu status como profissional do desenvolvimento pode te ajudar em sua carreira futura e também influencia na maneira como você lida com as situações. Alguém que está sempre escrevendo ganha habilidades importantes para o mercado de trabalho:</p><ul><li><strong>Aumenta a sua desenvoltura falada e escrita</strong>: portanto, melhora a sua forma de lidar com clientes e outros colegas, além disso, te ajuda a criar e expor argumentos de forma mais clara e concisa.</li><li><strong>Faz com que você tenha uma nova forma de pensar</strong>: O processo de criação de artigos é algo bastante elaborado, conforme vamos falar mais para frente, ele exige que você tenha uma organização e uma forma de construção concreta, você não vai conseguir escrever algo se simplesmente jogar suas ideias ao ar. Pensar na forma de escrita é uma ótima maneira de começar a ver o mundo de forma diferente</li><li><strong>Te prepara para feedbacks</strong>: Ao nos expormos na Internet estamos sujeitos todo tipo de resposta, boa ou ruim, e é importante sabermos lidar com ambas. Isso faz com que desenvolvamos a habilidade de saber responder críticas de forma ponderada e lidar com elogios de forma humilde.</li></ul><p>Já falamos muito do que escrever desenvolve como habilidade, mas, além disso, por que devemos começar a escrever?</p><h4>Consolida seu aprendizado</h4><p>Você provavelmente já ouviu a frase: <em>“Só aprende de verdade quem ensina a outra pessoa”</em>, ou alguma de suas variações, certo?</p><p>Isto é real. Ensinar outra pessoa força você a buscar correlações e consolidar o seu conhecimento. Uma forma bastante simples de fazer isso é escrevendo sobre ele.</p><p>Escrever faz com que você precise pensar na forma mais simples e fácil de fazer a pessoa que está lendo entender o conteúdo que você quer passar, isso ajuda a consolidar sua experiência e seu conhecimento de forma simples e rápida.</p><p>Ou então, se você é como eu, sua motivação para escrever artigos pode vir simplesmente da vontade de compartilhar conhecimento com outras pessoas e querer observar e analisar pontos de vista e opiniões diferentes das suas.</p><h4>Te mostra ao mundo</h4><p>Como profissional, é importante que outras pessoas saibam o que você está fazendo e como você se destaca nisso, você quer que as pessoas te conheçam pelo que? Se você tem um sonho de começar a trabalhar em uma empresa mas não sabe como alcançar este sonho, uma das opções é justamente <strong>aparecer mais</strong>, afinal, todas as empresas são compostas de pessoas e estas precisam saber que você existe.</p><p>Além de uma vantagem profissional, escrever também te coloca, invariavelmente, em um patamar de pessoas que, aos olhos de outras, se tornam referências sobre o que estão falando, pois muitos vão buscar seu conteúdo para completar trabalhos de escola ou até mesmo criar projetos pessoais.</p><p>Eu, até hoje, me lembro que, em 2010, quando estava começando a aprender a programar e, mais do que frequentemente, caia no site do grande <a href="http://macoratti.net/Default.aspx">José Carlos Macoratti</a> — na época, eu programava em <a href="https://docs.microsoft.com/dotnet/core/?WT.mc_id=blog-devto-ludossan">.NET</a> — e, até hoje, ele é uma das minhas maiores referências, mesmo não tendo tocado em um código <a href="https://docs.microsoft.com/dotnet/csharp/?WT.mc_id=blog-devto-ludossan">C#</a> há quase dez anos. Lembre-se da icônica frase de Isaac Newton:</p><blockquote><em>Se enxerguei mais longe foi porque me apoiei sobre os ombros de gigantes</em></blockquote><blockquote><em>- Isaac Newton</em></blockquote><p>Você pode ser um dos gigantes onde outros gigantes se apoiaram para desenvolver suas criações, você provavelmente nunca vai saber disso, mas outras pessoas — talvez aquela pessoa gerente de RH na empresa onde você tanto quer trabalhar — vão!</p><h4>Captura seus pensamentos</h4><p>Por último, mas não menos importante, escrever cria um repositório de conhecimento que pode ser a diferença entre o sucesso ou o fracasso de um projeto. Você não precisa escrever com o objetivo de se tornar famoso, mas sim para registrar o seu conhecimento para si, criando um arquivo de informações úteis que você pode buscar quando precisar.</p><blockquote><em>Um exemplo pessoal — que já me salvou mais de uma vez — é o meu </em><a href="https://github.com/khaosdoctor/my-notes"><em>repositório de notas</em></a><em>, um local simples que, há mais de três anos, escrevo todos os meus aprendizados de praticamente todos os cursos que já fiz em minha vida. Lá estão repositórios de exemplo, dicas e truques, ideias e até </em><a href="https://github.com/khaosdoctor/my-notes/blob/master/node/Guideline%20de%20palestra%20the%20conf%202019.pdf"><em>planejamentos inteiros de palestras</em></a><em>.</em></blockquote><p>Escrever sobre algo publicamente — ou mesmo de forma privada — te faz registrar e ter uma documentação mental por escrito daquele momento, pense como um <em>snapshot</em> do seu cérebro em uma determinada época.</p><h3>Por que devs não escrevem?</h3><p>Nós somos devs, não somos jornalistas ou repórteres, muito menos escritores profissionais. Tudo conspira para que o dev jamais escreva algo. Dentre os maiores problemas para devs na escrita temos:</p><ul><li><strong>Timidez</strong>: Devs são pessoas naturalmente introvertidas — muitas vezes é a razão pela qual escolhemos a profissão, máquinas são muito mais simples de lidar do que pessoas — e isso impacta duramente na expressão para um mundo todo através da Internet, mas isso pode ser superado, um exemplo muito legal é o artigo da <a href="https://dev.to/marianesantana/promises-in-15-minutes-9l7">Mariane Santana</a>, que nunca tinha escrito antes e agora está <a href="https://dev.to/devteam/the-7-most-popular-dev-posts-from-the-past-week-a8o">na lista dos 7 artigos mais lidos</a> do DEV Community 🤘</li><li><strong>Planejamento</strong>: Devs geralmente tem ansiedade elevada, mas não adianta querermos escrever tudo de uma vez, precisamos ter um planejamento</li><li><strong>Falta de prática</strong>: Infelizmente aqui não podemos mudar muita coisa a não ser dizer <em>a prática leva a perfeição…</em> Você só vai conseguir escrever melhor se praticar escrevendo cada vez mais. A leitura também ajuda muito a aumentar o seu vocabulário de escrita e melhorar a forma como você escreve</li><li><strong>Medo de ser criticado</strong>: Como já mencionei, quando nos expomos na Internet, estamos sujeitos a todo o tipo de crítica, então muitas pessoas não escrevem pelo receio de serem duramente criticadas. Neste caso, não temos muito o que fazer, apenas tenha pleno conhecimento do que você está escrevendo e se sinta pronto(a) para responder perguntas — não interagir também é uma opção.</li></ul><h3>Como escrever um artigo</h3><h4>Defina um assunto</h4><p>Você pode escrever literalmente sobre <strong>qualquer coisa</strong>. Que tal sobre a sua experiência usando uma ferramenta? Ou então o seu processo de aprendizado? Talvez sobre <a href="https://dev.to/womakerscode/career-in-programming-and-music-what-have-i-learned-from-all-this-3a7h">como seu estudo de música influenciou a sua vida como dev</a>. Enfim, você escolhe!</p><p>Ninguém vai esperar que você escreva sobre algo que está fora da sua área de atuação, e muito menos que você seja a referência máxima em algo que você está aprendendo (desde que você deixe claro que esteja aprendendo), você pode ficar desconfortável com isso, mas por outro lado, ter uma perspectiva sobre como <strong>VOCÊ</strong> faz as coisas e como é a <strong>SUA</strong> rotina pode ajudar outras pessoas a entender como serem melhores. Pontos de vista… sempre…</p><p>Outras ideias de escrita:</p><ul><li>Experiências próprias sobre algum assunto</li><li>Novidades sobre suas tecnologias favoritas</li><li>Histórias de vida, suas ou de outros</li><li>Aprendizados, fracassos, sucessos e lições que você teve em sua jornada</li></ul><h4>Pesquise e planeje</h4><p>Um passo muito importante da escrita técnica é a busca. Você provavelmente não vai ser o primeiro a escrever sobre alguma coisa. Mas pesquise <em>muito bem</em> antes de começar a escrita do seu tema. Tenha também um planejamento sobre o que você vai escrever:</p><ul><li><strong>Agende os seus artigos</strong>: Conforme eu falei <a href="https://dev.to/khaosdoctor/o-guia-definitivo-da-organizacao-pessoal-parte-1-44ea">em minha série sobre organização pessoal</a>, tudo precisa se tornar uma tarefa planejada, e com artigos não seria diferente, planeje as datas com antecedência</li><li><strong>Saiba seu tema</strong>: Saiba com antecedência sobre <em>o quê</em> você quer escrever</li><li><strong>Planeje seu conteúdo</strong>: Escreva em uma em qualquer lugar, um <em>outline</em> do seu artigo, com os principais títulos e tópicos</li></ul><p><strong>Veja se existem outros artigos com o mesmo tema.</strong> Não há problema em escrever artigos sobre a mesma coisa — da mesma forma como estou fazendo aqui — apenas se certifique que não é um plágio e deixe sempre referências para o conteúdo original. Veja algumas opções:</p><ul><li>Conteúdo da sua experiência pessoal sobre o tema</li><li>Uma opinião diferente</li><li>Um adendo ao tema (como este artigo)</li><li>Tradução para outra língua</li></ul><p>Procure o máximo possível de pontos de vista distintos sobre o mesmo assunto, quanto mais opiniões você incorporar, mais rico seu artigo ficará.</p><h4>Dando um título</h4><p>O título do seu artigo é o que fará com que as pessoas se interessem em lê-lo. Então ele precisa ser <strong>bem pensado</strong>. O título deve ser claro, conciso e deve indicar o propósito do artigo. Vou usar alguns exemplos do <a href="https://dev.to/iriskatastic/writing-technical-articles-tips-for-programmers-29gp#naming">artigo de referência</a> traduzidos:</p><ol><li><em>Roundup #58: Orleans 3.0, Snitch, Qual é o seu problema? Próximos 5 anos de ASP.NET Core</em></li><li><em>A Linguagem de Programação para Todas Comandar</em></li><li><em>Sofrendo de Débito Técnico?</em></li></ol><p>Veja por que estes exemplos são ruins:</p><ol><li>Muitas keywords, muita coisa acontecendo, o título é muito longo e parece que fala sobre absolutamente tudo</li><li>Muito “esotérico”, claramente parece um <em>clickbait</em>, mas não dá muito a entender sobre o que o material fala (isto também vale para o 3)</li><li>Uma pergunta ao leitor como um título não é um bom título. Ela gera dúvida e isso gera expectativa de resposta, se a resposta que você der não satisfazer o leitor, então você acabou de manchar a sua reputação para aquela pessoa específica.</li></ol><p>Agora alguns bons exemplos:</p><ol><li><a href="https://dev.to/womakerscode/career-in-programming-and-music-what-have-i-learned-from-all-this-3a7h">Carreira em Programação e Música: O que aprendi com isso tudo?</a></li><li><a href="https://dev.to/azure/desenvolvendo-uma-aplicacao-crud-node-js-com-postgresql-3clk">Desenvolvendo uma Aplicação CRUD Node.js com PostgreSQL</a></li><li><a href="https://dev.to/azure/tutorial-data-pipeline-using-mongodb-and-kafka-connect-on-kubernetes-eio">Tutorial: Data pipeline usado MongoDB e Kafka Connect no Kubernetes</a></li></ol><blockquote><em>Veja que o artigo da </em><a href="https://dev.to/anabneri"><em>Ana</em></a><em> sobre música contém uma pergunta, mas ela não é direcionada ao leitor, e sim uma pergunta retórica que remete a ela mesma, explicando do que o artigo se trata.</em></blockquote><h4>Estrutura</h4><p>Todo o artigo segue uma linha de raciocínio, isso é algo que aprendemos na escola em nossas primeiras redações, todo o texto possui:</p><ul><li>Título</li><li>Introdução</li><li>Desenvolvimento (Aqui é onde colocamos o código, imagens, exemplos)</li><li>Conclusão</li><li>Referências</li></ul><p>Com artigos técnicos não é diferente, a única coisa que será um pouco diferente é a parte das <em>Referências</em> que é geralmente utilizada como uma seção onde você pode ligar um artigo ao resto do seu trabalho.</p><h4>Tempo</h4><p>Um <a href="https://medium.com/data-lab/the-optimal-post-is-7-minutes-74b9f41509b">artigo da Data Lab</a> mostrou que o tempo ideal de um artigo é de sete a dez minutos. Isso não é uma regra, mas ajuda a te guiar para o tempo que seu conteúdo deve ter. Eu, particularmente, uso <a href="https://marketplace.visualstudio.com/items?itemName=gabrielepmattia.wordcount&amp;WT.mc_id=blog-devto-ludossan">uma extensão</a> para o <a href="https://code.visualstudio.com/?WT.mc_id=blog-devto-ludossan">VSCode</a> que me mostra estas informações.</p><p>Porém não há problema em passar deste tempo se você está escrevendo um artigo mais detalhado ou um tutorial, afinal, é o seu conteúdo. Apenas leve em consideração a capacidade de quebrá-lo em mais de um, formando uma série, como faço <a href="https://dev.to/khaosdoctor">com muitos artigos meus</a></p><h4>Conteúdo</h4><p>As pessoas esperam ler o conteúdo que você está dizendo que vai passar. Evite usar palavras muito complexas, ou escrever como se fosse um artigo científico — a não ser, claro, que este seja seu intuito.</p><p>Inclua imagens sempre que possível, <a href="https://gist.github.com">gists</a> e use o <a href="https://carbon.now.sh">Carbon</a> ou o <a href="https://codepen.io">CodePen</a> para incluir códigos.</p><p>Algumas outras dicas de conteúdo:</p><ul><li>Evite ser completamente informal, mas evite também ser muito formal</li><li>Veja aplicativos como o Grammarly (se estiver escrevendo em inglês), confie em extensões de analise de texto</li><li>Sempre revise seu texto antes de publicar</li><li>Veja se outra pessoa pode ler o texto para que ele possa ser revisado</li><li>Expanda acrônimos quando forem usados pela primeira vez no artigo, por exemplo: <em>“O uso do </em><a href="https://docs.microsoft.com/azure/aks/?WT.mc_id=blog-devto-ludossan"><em>AKS</em></a><em> (Azure Kubernetes Service) …”</em></li><li>Prefira utilizar a primeira pessoa do plural quando escrever artigos — a não ser que seja um artigo de opinião — pois “nós” cria um senso de coletividade com a pessoa que lê</li><li>Não faça parágrafos muito longos</li><li>Sempre que prover dados (números, etc), cite a data e fonte de onde eles vieram</li></ul><h4>Palavras-chave</h4><p>Keywords, ou palavras-chave, são importantes porque será através delas que mecanismos de busca vão indexar seu texto. Para procurar por quais tags você pode utilizar, veja alguns exemplos diretos do artigo que referenciei:</p><ul><li>moz.com</li><li>app.neilpatel.com</li><li>trends.google.com</li><li>keywordtool.io</li><li>wordtracker.com/scout</li></ul><p>Use pelo menos 5 tags para indicar o que você está escrevendo, em nível decrescente de mais genérico para mais específico, por exemplo: open-source, back-end, javascript, node.js, cli</p><h4>Divulgação</h4><p>Não tenha medo de divulgar o seu trabalho da maior forma possível, utilize ferramentas como o <a href="https://mlabs.com.br">MLabs</a>, <a href="https://hootsuite.com">Hootsuite</a> e outras para fazer a gestão de suas redes sociais mais utilizadas, agende seus posts. Faça uso de grupos em redes sociais que sejam especializados no que você está escrevendo, seu público está por lá.</p><blockquote><strong><em>Dica</em></strong><em>: Mantenha postagens recorrentes sobre seu artigo, mas evite duplicar o texto, muitos algoritmos de redes sociais identificam posts duplicados na mesma conta como spam.</em></blockquote><h3>Conclusão</h3><p>Tentei escrever de forma mais simples e concisa tudo o que aprendi escrevendo artigos técnicos nestes últimos anos. Espero que, com estas dicas, você também se inspire a escrever mais e compartilhar o seu conteúdo.</p><p>Não deixe de acompanhar mais do meu conteúdo no <a href="https://blog.lsantos.dev/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=dev_articles">meu blog</a> e se inscreva na <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=dev_articles">newsletter</a> para receber notícias semanais!</p><p>Se você quer começar a escrever mas não sabe se seu artigo está legal e quer uma segunda olhada ou então você já escreve e quer compartilhar, me manda seu conteúdo através das minhas redes sociais, eu adoraria dar uma olhada!</p><p>Você pode encontrar todas elas no meu site:</p><p><a href="https://lsantos.dev">Lucas Santos</a></p><p>Até mais!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1f8b9ba74d4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Do zero à automação em 7 minutos com GitHub Actions]]></title>
            <link>https://medium.com/@khaosdoctor/do-zero-%C3%A0-automa%C3%A7%C3%A3o-em-7-minutos-com-github-actions-ca08364e8c36?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/ca08364e8c36</guid>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[github]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Fri, 08 May 2020 01:41:54 GMT</pubDate>
            <atom:updated>2020-07-31T17:50:10.347Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NdiZO-PYsnWPq6w3tnHi7Q.jpeg" /><figcaption>Photo by Josh Redd on Unsplash</figcaption></figure><h3>Do zero à automação de containers em 7 minutos</h3><p>Em um <a href="https://dev.to/azure/construindo-e-publicando-um-backend-graphql-completo-sem-escrever-uma-linha-de-codigo-g40">artigo anterior</a>, comentei como poderíamos criar um backend GraphQL completo apenas usando uma imagem Docker e um arquivo de configuração. Tudo isso hospedado no <a href="https://docs.microsoft.com/azure/container-instances/?WT.mc_id=blog-devto-ludossan">Azure</a>. Agora vamos aprender a automatizar os deploys que são feitos para a nossa hospedagem e a atualização automática do nosso backend!</p><p>Este projeto todo visa criar um backend para meu futuro arquivo de conteúdo que estará presente no <a href="https://lsantos.dev">meu site</a>. Mas sempre que eu atualizar o backend ou mudar o schema do GraphQL vou ter que fazer todo o deploy do serviço novamente.</p><p>Então, ao invés disso, eu gostaria que, a cada push no meu branch master, eu gerasse uma nova versão do arquivo e enviasse a atualização para o <a href="https://portal.azure.com/?WT.mc_id=blog-devto-ludossan">Azure</a>. Porém eu não quero ter que usar outras ferramentas para isso, quero manter toda a stack o mais simples possível, como estamos utilizando somente o GitHub e o <a href="https://portal.azure.com/?WT.mc_id=blog-devto-ludossan">Azure</a>, nada mais justo do que continuar no GitHub para fazer a automação, não é mesmo?</p><p>Por isso que vamos usar os <a href="https://azure.microsoft.com/blog/github-actions-for-azure-is-now-generally-available/?WT.mc_id=blog-devto-ludossan"><strong>GitHub Actions</strong></a></p><p>Similar a outros provedores de CI como o Travis ou o Circle, o GitHub também possui um sistema de integração e automação do repositório. A facilidade deste sistema em relação aos outros é que você já está dentro do mesmo repositório e ja está dentro da mesma ferramenta.</p><p>O projeto que vamos utilizar para automatizar será o <a href="https://github.com/khaosdoctor/site-backend">meu repositório publico de backend</a>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*H2hIsW4sxqkQ0-ck" /></figure><p>Veja que o repositório é super simples, não contém muitos arquivos, apenas um docker-compose.yml para testes locais e o nosso arquivo yml do mongoke que será o schema GraphQL.</p><p>No topo da página, ao lado de settings, temos o botão Actions, será lá que vamos configurar a nossa automação!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tmZFH1SNxaXB4AjI" /></figure><p>O GitHub já possui uma série de workflows prontos, porém, infelizmente, ainda não temos nenhum para o que queremos fazer. Mas o que queremos fazer? Para deixar tudo mais claro, vamos listar todas as etapas do processo:</p><ol><li>Recebemos um push na branch master</li><li>Conectamos com a <a href="https://portal.azure.com/?WT.mc_id=blog-devto-ludossan">Azure</a> através de um <a href="https://docs.microsoft.com/azure/app-service/deploy-github-actions?WT.mc_id=blog-devto-ludossan#create-a-service-principal">service principal</a> e de uma action chamada <a href="https://github.com/Azure/login">Azure/login</a></li><li>Depois vamos utilizar a action <a href="https://github.com/Azure/CLI">Azure/cli</a> para poder executar o comando de deploy do nosso container</li></ol><p>Alguns detalhes que temos que perceber:</p><ul><li>O cache do GitHub Pages é um pouco longo, então vamos ter que gerar um número aleatório ou uma fingerprint para colocar na URL do nosso arquivo YAML do Mongoke</li><li>Temos uma URI do MongoDB que deve ser um segredo</li></ul><h3>Criando um secret</h3><p>Primeiramente vamos tratar das nossas dependências, a dependência mais fácil de ser resolvida é criar a URL do MongoDB como um secret. Para isso vamos até a nossa aba Settings e clicamos em Secrets. Basta adicionar um novo secret clicando no link Add new Secret e preencher um nome e o conteúdo do mesmo. No caso estamos adicionando a URI do MongoDB, então vamos chamar este secret de MONGODB_URI</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tDihPBBGIwg-3U2-" /></figure><h3>Permitindo ações na Azure</h3><p>Para que possamos permitir que o GitHub execute ações em nosso nome na <a href="https://portal.azure.com/?WT.mc_id=blog-devto-ludossan">Azure</a>, vamos precisar criar um <a href="https://docs.microsoft.com/azure/app-service/deploy-github-actions?WT.mc_id=blog-devto-ludossan#create-a-service-principal">Service Principal</a>. Este recurso nos dá uma conta de aplicação para que possamos permitir que outras pessoas ou serviços conectem-se em nosso portal e realize ações em nosso nome.</p><p>Para criarmos este recurso, vamos usar o <a href="https://docs.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest&amp;WT.mc_id=blog-devto-ludossan">Azure CLI</a>. Depois de realizar o login com o comando az login, basta executar o seguinte comando:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%28171%252C184%252C195%252C0%29%26t%3Ddracula%26wt%3Dnone%26l%3Dauto%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DFira%2520Code%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Daz%252520ad%252520sp%252520create-for-rbac%252520--name%252520%25253Cnome-do-SP%25253E%252520--role%252520contributor%252520--scopes%252520%25252Fsubscriptions%25252F%25253CID-da-subscription%25253E%25252FresourceGroups%25252F%25253Cnome-do-resourcegroup%25253E%252520--sdk-auth&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%28171%25252C184%25252C195%25252C0%29%26t%3Ddracula%26wt%3Dnone%26l%3Dauto%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DFira%252520Code%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Daz%25252520ad%25252520sp%25252520create-for-rbac%25252520--name%25252520%2525253Cnome-do-SP%2525253E%25252520--role%25252520contributor%25252520--scopes%25252520%2525252Fsubscriptions%2525252F%2525253CID-da-subscription%2525253E%2525252FresourceGroups%2525252F%2525253Cnome-do-resourcegroup%2525253E%25252520--sdk-auth&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/aa9c235ecad3405651361d7530714ce2/href">https://medium.com/media/aa9c235ecad3405651361d7530714ce2/href</a></iframe><p>Para obter o ID da sua subscription basta executar o comando az account list e buscar a chave id da subscription que estiver com a chave isDefault marcada como true. Você também pode dar qualquer nome ao seu Service Principal, faça com que ele seja descritivo para saber a quem você está garantindo permissões.</p><p>Por último, outro detalhes importante é somente dar as permissões aos recursos necessários, ou seja, não vamos criar um SP para a conta <strong>TODA</strong>, mas sim somente para o <em>Resource Group</em> que precisamos, no meu caso, este RG se chama personal-website e é onde eu estou agrupando todos os recursos relativos ao meu site.</p><p>Este comando deve dar uma resposta em JSON do tipo:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%28171%252C184%252C195%252C0%29%26t%3Ddracula%26wt%3Dnone%26l%3Dapplication%252Fjson%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DFira%2520Code%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%25257B%25250A%252520%252520%252522clientId%252522%25253A%252520%252522xxxxxxx%252522%25252C%25250A%252520%252520%252522clientSecret%252522%25253A%252520%252522xxxxxxxx%252522%25252C%25250A%252520%252520%252522subscriptionId%252522%25253A%252520%252522xxxxxxxx%252522%25252C%25250A%252520%252520...%25250A%25257D&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%28171%25252C184%25252C195%25252C0%29%26t%3Ddracula%26wt%3Dnone%26l%3Dapplication%25252Fjson%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DFira%252520Code%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%2525257B%2525250A%25252520%25252520%25252522clientId%25252522%2525253A%25252520%25252522xxxxxxx%25252522%2525252C%2525250A%25252520%25252520%25252522clientSecret%25252522%2525253A%25252520%25252522xxxxxxxx%25252522%2525252C%2525250A%25252520%25252520%25252522subscriptionId%25252522%2525253A%25252520%25252522xxxxxxxx%25252522%2525252C%2525250A%25252520%25252520...%2525250A%2525257D&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/2b59e984ec44c9ca858b2aa1de7c37a9/href">https://medium.com/media/2b59e984ec44c9ca858b2aa1de7c37a9/href</a></iframe><p>Copie toda essa resposta e crie um novo secret no GitHub chamado AZURE_CREDENTIALS:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/765/0*Oq5QyAMOR7AC7i1Q" /></figure><p>Agora devemos ter dois secrets criados:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*q-RIyElRwFa3zXTU" /></figure><h3>Criando um workflow</h3><p>Para criarmos o nosso primeiro workflow, basta voltarmos para o painel Actions ao lado de Settings e clicar no botão Set up this workflow:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*diYWMxG2TpJ82Sm1" /></figure><p>Esta ação deve levar para uma nova tela com um modelo inicial do código:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*v0JNCLX9rAi2SaRx" /></figure><p>Perceba que as GitHub Actions nada mais são do que um arquivo YAML que ficam em uma pasta &lt;seu repo&gt;/.github/workflows e este será o nome do workflow, portanto é importante que ele seja bastante descritivo. Vamos dar o nome do nosso de publish-prod.yml:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/537/0*i8B-FwSTTrZuKtlV" /></figure><p>O conteúdo que temos é o seguinte:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zExvrp9-LVopTWg5oYZmcQ.png" /></figure><p>Primeiro, vamos setar um nome para nosso workflow, este será o nome que aparecerá na UI do GitHub, então podemos deixar um pouco mais bonito:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jc3jshv1vu5YrAVs8ZHofQ.png" /></figure><p>Agora vamos definir em qual tipo de ação queremos que ele rode. Como queremos que ele rode em qualquer push ou PR no branch master, não vamos fazer nenhuma alteração, mas se precisássemos alterar esta ação, teríamos que mudar a chave on. Para verificar quais são as ações disponíveis, basta clicar no editor e apertar ENTER que um <em>intellisense</em> será mostrado:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/290/0*bxe5Hg2B5h7kiL7s" /></figure><blockquote><em>Veja que temos várias ações possíveis, para detalhar melhor, veja </em><a href="https://help.github.com/en/actions/reference/events-that-trigger-workflows#webhook-events"><em>a documentação oficial</em></a></blockquote><p>Então vamos criar uma seção para compartilhar variáveis de ambiente e conteúdos que vamos usar na parte mais a frente do nosso script, por exemplo, o nome do container e também o nome do resource group. Para isso criamos um bloco env:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c-TtD9itKom4CReF-j8i6g.png" /></figure><p>Agora vamos, de fato, criar a máquina e o que chamamos de runner, que é quem vai rodar o nosso código de testes e de publicação. Todo o workflow é composto de um ou mais jobs que podem ser executados sequencialmente ou em paralelo. No nosso caso não temos que fazer muita coisa, apenas conectar e fazer o deploy. Portando vamos modificar a seção jobs:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D2e42TJpQIis6nKobJBM8w.png" /></figure><p>Iremos rodar nosso job em uma imagem do Ubuntu, veja as demais opções <a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on">na documentação</a>. Depois disso vamos configurar os nossos steps, um step é uma sequencia de tarefas que serão executadas como partes de um job, ou seja, aqui será o lugar onde vamos executar o nosso login e o nosso deploy!</p><p>Para isso vamos modificar a chave steps e vamos remover todo o conteúdo, então vamos utilizar os steps pré prontos da Azure para poder fazer o login! Podemos pesquisar na caixa de texto lateral por Azure/login:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4bBSYavjQ9JDtefl" /></figure><p>Ao clicar no nome do step, vamos ter um tutorial de como usá-lo, neste caso é bastante simples, basta copiarmos o seguinte código para dentro do nosso step:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/446/1*fXK7Z3X6sywzaANHPrjx2A.png" /></figure><p>E será aqui que vamos setar o nosso primeiro secret, precisamos do AZURE_CREDENTIALS que criamos anteriormente para poder prover as credenciais de acesso para o login. Para usar um secret basta colocá-lo entre chaves duplas como em: ${{ secrets.AZURE_CREDENTIALS }}.</p><p>Nosso arquivo está assim:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dGzjWLVNondqNe0LielzuQ.png" /></figure><p>Vamos criar um segundo step que vamos chamar de Deploy ACI, que será aonde vamos executar o nosso comando do Azure CLI. Para isso vamos utilizar outro step com o comando run que vai conter a linha que vamos executar em nosso CLI. Em nosso a caso, já com as variáveis substituídas, será esta:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YIcLMWWZALr7IO2Ws26ClA.png" /></figure><p>Vamos observar bem a última parte onde criamos a variável MONGOKE_CONFIG_URL, esta variável sozinha não vai ser suficiente, temos que concatená-la com o valor do SHA do commit para não sofrermos com o cache. Para concatenarmos variáveis, temos que trabalhar somente no nível de steps, pois expressões não são permitidas a níveis de jobs ou do workflow como um todo. Vamos então criar um outro step e chamá-lo de Create URL.</p><p>Neste step, vamos executar uma <a href="https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#using-workflow-commands-to-access-toolkit-functions"><strong>Workflow Command</strong></a>, que são comandos nativos do GitHub Actions, que podem ser acessados com a sintaxe ::comando. No nosso caso vamos usar o comando set-env, que serve para criar uma nova variável de ambiente, a sintaxe é a seguinte: ::set-env name=&lt;nome&gt;::&lt;valor&gt; e ai poderemos utilizar as expressões de concatenação:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wWgcaTtCATvy1NJwauG-cA.png" /></figure><p>Veja que estamos concatenando ${{ env.MONGOKE_CONFIG_URL }} com ?v= e pegando os seis primeiros digitos de uma <a href="https://help.github.com/pt/actions/configuring-and-managing-workflows/using-environment-variables">variável nativa de ambiente</a> chamada GITHUB_SHA. Vamos adicionar este step após o login:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6NT5gNCwS6FEm5tEaNkfqw.png" /></figure><p>Agora vamos para o último passo, onde vamos executar o nosso comando, com uma pequena alteração, ao invés de utilizar a variável MONGOKE_CONFIG_URL, vamos trocar para a nossa nova variável MONGOKE_URL:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qqSANiai1089JDWGBh3O8Q.png" /></figure><p>Nosso arquivo final será assim:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UrbN1mgPw9gYrP-XNJVBWg.png" /></figure><p>Agora podemos clicar no botão Start Commit e esperar o nosso action ser executado:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/427/0*hctS-h0CJzc1EjO2" /></figure><h3>Acompanhando o workflow</h3><p>Podemos acompanhar o nosso workflow na aba Actions:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1004/0*wb7qZMr5Ajrjx__0" /></figure><p>Lembrando que <strong>workflows não executados nenhuma vez não vão aparecer na lista</strong>. Basta clicarmos no nome do workflow para podermos ver o que aconteceu:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mB8OsipEyxiwF3Bg" /></figure><p>Agora podemos observar no portal da Azure que nosso container foi atualizado:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/991/0*5UcnCdFkuZaJQmzL" /></figure><p>E que também nossas URLs estão corretas:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/765/0*sH4PNq2JApgDUMwd" /></figure><h3>Conclusão</h3><p>Em 7 minutos automatizamos todo o processo de deploy da nossa aplicação para a Azure e, sem perceber, ganhamos muito tempo com uma ação simples e rápida que pode ser feita em poucos minutos!</p><p>Em outros artigos vamos explorar ainda mais os GitHub Actions para outras ferramentas e modelos de aplicação distribuída!</p><p>Não deixe de acompanhar mais do meu conteúdo no <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=medium_post">meu blog</a> e se inscreva na <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=medium_post">newsletter</a> para receber notícias semanais!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ca08364e8c36" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Javascript: Entendendo Async Iterators]]></title>
            <link>https://medium.com/trainingcenter/javascript-entendendo-async-iterators-8322fc7106db?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/8322fc7106db</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[iterators]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Sun, 28 Jul 2019 16:48:47 GMT</pubDate>
            <atom:updated>2020-07-31T17:50:04.678Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cxVWSudHBzeRHKB7uWl8Lw.jpeg" /></figure><p>Há um tempo atrás, fiz <a href="https://medium.com/trainingcenter/iterators-em-javascript-880adef14495">um post</a> em meu Medium onde falo tudo sobre o protocolo Iterator e sua interface de uso. Porém, além de APIs como Promise.finally, o ECMAScript 2018 trouxe para a gente uma outra forma de tratarmos os nossos iterators. Os <a href="https://github.com/tc39/proposal-async-iteration"><em>async iterators</em></a>.</p><h3>O problema</h3><p>Vamos nos colocar em uma situação bastante comum. Estamos trabalhando com Node.js e temos que ler um arquivo, linha a linha. O Node possui uma API para este tipo de função chamada readLine (veja a documentação completa <a href="https://nodejs.org/api/readline.html">aqui</a>), esta API é um wrapper para que você possa ler dados de uma stream de entrada linha a linha ao invés de ter que fazer o <em>parsing</em> do buffer de entrada e quebrar o texto em pequenas partes.</p><p>Ela expõe uma API de eventos, que você pode escutar desta forma:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9aadcb167a664b850170b53296fb222a/href">https://medium.com/media/9aadcb167a664b850170b53296fb222a/href</a></iframe><p>Imagine que tenhamos um arquivo simples:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b30491c9a32bf404953fc059f7be43e3/href">https://medium.com/media/b30491c9a32bf404953fc059f7be43e3/href</a></iframe><p>Se rodarmos este código no arquivo que criamos, teremos um output linha a linha no nosso console. Porém, trabalhar com eventos não é uma das melhores formas de fazer código manutenível, pois eventos são completamente assíncronos e eles podem quebrar o fluxo do código, uma vez que eles são disparados fora de ordem e você só consegue atribuir uma ação através de um listener.</p><h3>A Solução</h3><p>Além da API de eventos, o readline também expõe um async iterator. Isso significa que, ao invés de fazermos a leitura da linha através de listeners no evento line, vamos fazer a leitura da linha através de uma nova forma de utilização da keyword for.</p><p>Hoje temos algumas opções de uso para um laço de repetição for, a primeira delas é o modelo mais comum, utilizando um contador e uma condição:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/045f6ed779fbbafc67faa4688a28d235/href">https://medium.com/media/045f6ed779fbbafc67faa4688a28d235/href</a></iframe><p>Também podemos utilizar a notação for ... in para leitura de índices de arrays:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a9655ec64f03331752039885adcd78d2/href">https://medium.com/media/a9655ec64f03331752039885adcd78d2/href</a></iframe><p>No caso anterior, vamos ter como saída no console.log, os números de 1 a 6, porém se utilizarmos console.log(index) vamos logar o índice do array, ou seja, os números de 0 a 5.</p><p>Para o próximo caso, podemos usar a notação for ... of para pegar diretamente as propriedades enumeráveis do array, ou seja, seus valores diretos:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fdb9eae780eac8b4b1c890ea9ae57e52/href">https://medium.com/media/fdb9eae780eac8b4b1c890ea9ae57e52/href</a></iframe><p>Perceba que todas as formas de utilização que descrevi são síncronas, ou seja, como fazemos para que possamos ler uma sequencia de promises em ordem?, imagine que tenhamos uma outra interface que nos retorne sempre uma Promise, que se resolve para a nossa linha do arquivo em questão. Para resolvermos essas promises em ordem, temos de fazer algo assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cf9b0626fe9efdd92ea9cdc0b973dc73/href">https://medium.com/media/cf9b0626fe9efdd92ea9cdc0b973dc73/href</a></iframe><p>Porém, graças a magia dos async iterables (como o readline) nós podemos fazer o seguinte:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6baa12f8bc3c7c500283e6b67484a0cb/href">https://medium.com/media/6baa12f8bc3c7c500283e6b67484a0cb/href</a></iframe><p>Perceba que agora estamos usando uma nova definição de for, o for await (const x of y).</p><h3>For Await e Node.js</h3><p>A notação for await é nativamente suportada no runtime do Node.js da versão 10.x. Se você está usando as versões 8.x ou 9.x então você precisa iniciar o seu arquivo Javascript com a flag --harmony_async_iteration. Infelizmente os async iterators não são suportados nas versões 6 ou 7 do Node.js.</p><p>Para podermos entender o conceito de async iterators, precisamos dar uma recapitulada sobre o que são iterators em si. O meu artigo anterior é uma fonte maior de informações, mas, em suma, um Iterator é um objeto que expõe uma função next() que retorna um outro objeto com a notação {value: any, done: boolean} onde value é o valor da iteração atual e done identifica se há ou não há mais valores na sequencia. Um exemplo simples, é um iterador que passa por todos os itens de um array:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/93d5dddb1faabf17e5e728ced2aae170/href">https://medium.com/media/93d5dddb1faabf17e5e728ced2aae170/href</a></iframe><p>Sozinho, um iterator não tem nenhuma utilidade prática, para que possamos tirar algum proveito dele, precisamos de um iterable. Um iterable é um objeto que possui uma chave Symbol.iterator que retorna uma função, a qual retorna nosso iterador:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/86313b2e5772209340a5c27c097eeb3f/href">https://medium.com/media/86313b2e5772209340a5c27c097eeb3f/href</a></iframe><p>Agora podemos utilizar ele normalmente, com for (const x of iterable) e teremos todos os valores do array sendo iterador um a um.</p><blockquote><em>Se você quiser saber um pouco mais sobre </em><strong><em>Symbols</em></strong><em>, dê uma olhada </em><a href="https://medium.com/trainingcenter/javascript-symbols-decifrando-o-mist%C3%A9rio-383e359e64e3"><em>neste outro artigo que escrevi só sobre o tema</em></a></blockquote><p>Por baixo dos panos, todos os arrays e objetor possuem um Symbol.iterator para que possamos fazer for (let x of [1,2,3]) e retornar os valores que queremos.</p><p>Como é de se esperar, um async iterator é exatamente igual a um iterator, exceto que, ao invés de um Symbol.iterator, temos um Symbol.asyncIterator em nosso iterable e, ao invés de um objeto que retorna {value, done} teremos uma Promise que resolve para um objeto com a mesma assinatura.</p><p>Vamos transformar nosso iterator acima em um async iterator:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a544f3995c2f7f923cab1c258bb3ad36/href">https://medium.com/media/a544f3995c2f7f923cab1c258bb3ad36/href</a></iframe><h3>Iterando assincronamente</h3><p>Podemos fazer a iteração de qualquer iterator de forma manual, chamando a função next():</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f109999aa5045ca85bd6c4ee70565347/href">https://medium.com/media/f109999aa5045ca85bd6c4ee70565347/href</a></iframe><p>Para que possamos iterar através do nosso async iterator, temos que utilizar for await, porém, lembre-se que a keyword await só pode ser usada dentro de uma async function, ou seja, temos que ter algo deste tipo:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0c13126990b7a982c9ece3dac3020f84/href">https://medium.com/media/0c13126990b7a982c9ece3dac3020f84/href</a></iframe><p>Mas, assim como os iteradores assíncronos, não são suportadas no Node 8.x ou 9.x, para podermos utilizar um async iterator nessas versões, nós podemos simplesmente extrair o next dos seus objetos e iterar por eles manualmente:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/643b86fc6275b29aba9763a2171a08d4/href">https://medium.com/media/643b86fc6275b29aba9763a2171a08d4/href</a></iframe><p>Perceba que for await é muito mais clean e muito mais conciso porque se comporta como um loop comum, mas também, além de ser muito mais simples de entender, checa pelo final do iterador sozinho, através da chave done.</p><h4>Tratando erros</h4><p>O que acontece se nossa promise for rejeitada dentro do nosso iterador? Bem, como qualquer promise rejeitada, podemos pegar seu erro através de um simples try/catch (já que estamos usando await ):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6643200c754544dc49285de8534a847b/href">https://medium.com/media/6643200c754544dc49285de8534a847b/href</a></iframe><h3>Fallbacks</h3><p>Algo bastante interessante dos async iterators é que eles possuem um fallback para Symbol.iterator, isso significa que você pode utilizar ele também com seus iteradores comuns, por exemplo, um array de promises:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/190dec1b76acd07372d9254f6cb49e98/href">https://medium.com/media/190dec1b76acd07372d9254f6cb49e98/href</a></iframe><h3>Async Generators</h3><p>Em grande parte, iterators e async iterators podem ser criados a partir de <a href="https://medium.com/trainingcenter/javascript-entendendo-generators-408cbce9aee">generators</a>. Generators são funções que permitem que suas execuções sejam pausadas e retomadas, de forma que é possível realizar uma execução e depois buscar um próximo valor através de uma função next().</p><blockquote><em>Esta é uma descrição muito simplificada de generators, a leitura do artigo que fala somente sobre eles é imprescindível para que você possa entender generators de forma rápida e mais profunda.</em></blockquote><p>Async generators se comportam como um async iterator, porém, você deve implementar o mecanismo de parada manualmente, por exemplo, vamos construir um gerador de mensagens aleatórias para commits do git para deixar seus colegas super felizes com suas contribuições:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bff19eb9373328e12d2a6adcf9d9f103/href">https://medium.com/media/bff19eb9373328e12d2a6adcf9d9f103/href</a></iframe><p>Veja que não estamos em nenhum momento retornando um objeto {value, done}, então o loop não tem como saber quando a execução terminou. Podemos implementar uma função desta forma:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/63957c57ddc9af9e8b201b8ed4881d16/href">https://medium.com/media/63957c57ddc9af9e8b201b8ed4881d16/href</a></iframe><h3>Caso de uso</h3><p>Para exemplificar de forma mais interessante, vamos construir um async iterator para um caso de uso real. Atualmente, o driver do Oracle Database para Node.js suporta uma API de resultSet, que executa uma query no banco de dados e retorna uma stream de registros que podem ser lidos um a um através do método getRow().</p><p>Para criarmos esse resultSet precisamos executar uma query no banco, desta forma:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4a96926a4bdc08ab815bcf7a07219978/href">https://medium.com/media/4a96926a4bdc08ab815bcf7a07219978/href</a></iframe><p>Nosso resultSet possui um método chamado getRow() que nos retorna uma Promise da próxima linha do banco que deve ser trazida, isso é um belo caso de uso para um async iterator não é? Podemos criar um cursor que nos retorna este resultSet linha por linha. Vamos deixar um pouco mais complexo através da criação de uma classe Cursor:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5d3e62c63023ab073c9a4b644d84d03f/href">https://medium.com/media/5d3e62c63023ab073c9a4b644d84d03f/href</a></iframe><p>Veja que o cursor recebe o resultSet que ele deve trabalhar e o armazena em seu estado atual. Então vamos alterar nosso método anterior para que retornemos o cursor ao invés do resultSet de uma única vez:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/43c18ec5e9c03541967d4ef7976d767a/href">https://medium.com/media/43c18ec5e9c03541967d4ef7976d767a/href</a></iframe><p>Desta forma podemos fazer um loop por todas as nossas linhas retornadas sem precisar de uma resolução de Promises individual.</p><h3>Conclusão</h3><p>Async iterators são extremamente poderosos, especialmente em linguagens dinâmicas e assíncronas como o Javascript, com eles você pode transformar uma execução complexa em um código simples, escondendo a maioria da complexidade do usuário.</p><p>Não deixe de acompanhar mais do meu conteúdo no <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=medium_post">meu blog</a> e se inscreva na <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=medium_post">newsletter</a> para receber notícias semanais!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8322fc7106db" width="1" height="1" alt=""><hr><p><a href="https://medium.com/trainingcenter/javascript-entendendo-async-iterators-8322fc7106db">Javascript: Entendendo Async Iterators</a> was originally published in <a href="https://medium.com/trainingcenter">Training Center</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Entendendo Promises de uma vez por todas]]></title>
            <link>https://medium.com/trainingcenter/entendendo-promises-de-uma-vez-por-todas-32442ec725c2?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/32442ec725c2</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Mon, 08 Apr 2019 14:01:01 GMT</pubDate>
            <atom:updated>2020-11-17T14:24:29.941Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*qd397CiUFnmsbH2H.png" /><figcaption>Créditos: fransciskim.com</figcaption></figure><p>Promises são um conceito essencial do JavaScript. Elas estão presentes em praticamente todo o ecossistema da linguagem.</p><p>Promises são um padrão de desenvolvimento que visam representar a conclusão de operações assíncronas. Elas não eram nativas do JavaScript até o ES6, quando houve uma implementação oficial na linguagem, antes delas, a maioria das funções usavam <em>callbacks.</em></p><p>Neste artigo vamos estudar a fundo o que é uma Promise e como podemos entender seu funcionamento.</p><blockquote>Vamos usar uma ferramenta muito legal chamado <a href="http://bevacqua.github.io/promisees/">Promisees</a> criada pelo Nicolás Bevacqua para podermos visualizar a execução de nossas Promises em tempo real de forma visual.</blockquote><p>Antes de começar, queria deixar um <em>disclaimer</em> de que, apesar de saber que um artigo no Medium deveria ter mais ou menos 7 minutos para não ficar chato, este artigo é bem profundo e explicativo, então eu vou tentar colocar tudo o possível sobre Promises aqui para servir de guia para outros que possam estar procurando!</p><h3>História das Promises</h3><p>Promises remontam à década de 70 — como você pode ver <a href="https://en.wikipedia.org/wiki/Futures_and_promises">neste</a> artigo — e eram chamadas de <strong>futures, deferred ou delays</strong>. Pelo artigo, elas são definidas como:</p><blockquote>Construtos usados para sincronizar a execução de um programa em linguagens de programação concorrentes. Eles descrevem um objeto que atua como um proxy para um resultado que é, inicialmente, desconhecido devido a sua computação não estar completa no momento da chamada.</blockquote><p>De acordo com o que vemos na Internet, no JavaScript, as Promises fizeram sua primeira aparição em 2007 em uma biblioteca chamada MochiKit . Depois outras bibliotecas como o Dojo e o jQuery adotaram a mesma especificação pouco tempo depois.</p><p>Por fim, para padronizar todas as implementações, o grupo <em>CommonJS</em> escreveu a especificação chamada <a href="https://promisesaplus.com/">Promises/A+</a> que visava ditar todas as regras necessárias para definir o que era uma Promise e sua interoperabilidade com outros sistemas.</p><p>No caso do NodeJS, nas primeiras versões, o runtime já implementava nativamente Promises, que foram removidas em favor de callbacks (que é a forma como conhecemos NodeJS no início), depois do lançamento do ES6 a plataforma implementou nativamente a funcionalidade de Promises que já estava implementada no V8 desde algum tempo antes. Isto porque o padrão ES6 já implementa o modelo A+, que descrevemos antes, de forma nativa, portanto a grande maioria dos browsers já permite o uso de Promises sem nenhum tipo de biblioteca externa.</p><h3>Fluxo Assíncrono</h3><p>O JavaScript por si só é tido como uma linguagem que tem que lidar com várias chamadas e execuções que não acontecem no momento que o programador executou o código, por exemplo, a leitura de um arquivo no NodeJS de forma síncrona:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/423831f17d3b9abaa833c8feebd93aa7/href">https://medium.com/media/423831f17d3b9abaa833c8feebd93aa7/href</a></iframe><p>Esta função é uma função síncrona, ou seja, quando a chamarmos, vamos pausar o que quer que esteja sendo executado e vamos realizar este processamento, depois vamos retornar o valor final. Desta forma estamos fazendo uma operação completamente síncrona. No nosso caso, vamos parar a execução do programa para buscar e ler o arquivo e depois vamos retornar seu resultado ao fluxo normal do programa.</p><p>Como queremos que nossas operações e nosso código rodem o mais rápido possível, queremos paralelizar o máximo de ações que conseguirmos. Ações de leitura de arquivos são consideradas lentas porque I/O é sempre mais lento que processamento em memória, vamos paralelizar a nossa função dizendo que queremos ler o arquivo de forma assíncrona:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c910c1f6aa7e55c672ff688c1b0e7762/href">https://medium.com/media/c910c1f6aa7e55c672ff688c1b0e7762/href</a></iframe><p>Agora o que estamos fazendo é passando um <em>callback</em> para a função <em>readFile</em> que deverá ser executado <strong>após</strong> a leitura do arquivo. Em essência — e abstraindo muito a funcionalidade — o que a função readFile faz é algo assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/80dc75daefb89b95e54e83cfee3e8401/href">https://medium.com/media/80dc75daefb89b95e54e83cfee3e8401/href</a></iframe><p>Basicamente estamos registrando uma ação que vai ser executada após uma outra ação ser concluída, mas não sabemos quando essa ação será concluída. O que sabemos é apenas que em um momento ela será concluída, então o JavaScript utiliza o EventLoop — que não vamos cobrir neste artigo, mas vocês podem pesquisar <a href="https://medium.com/reactbrasil/como-o-javascript-funciona-o-event-loop-e-o-surgimento-da-programa%C3%A7%C3%A3o-ass%C3%ADncrona-5-maneiras-de-18d0b8d6849a">aqui</a> e <a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/EventLoop">aqui</a> — para registrar um callback, basicamente o que estamos dizendo é: &quot;Quando função X acabar, execute Y e me dê o resultado&quot;. Então estamos delegando a resolução de uma computação para outro método.</p><h4>Uma outra opção</h4><p>Muitas outras APIs nos fornecem uma outra opção ao se trabalhar com o fluxo assíncrono: os eventos.</p><p>Eventos são muito presentes no JavaScript, no front-end, quando escutamos por eventos de click em um botão com elemento.addEventListener ou no NodeJS quando podemos executar, por exemplo, um fetch que busca dados de uma API:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ddc053278c93e368fed54d59a7bceb33/href">https://medium.com/media/ddc053278c93e368fed54d59a7bceb33/href</a></iframe><p>O problema com a API de eventos é que o código fica literalmente solto, então é difícil manter uma linearidade de pensamento porque o código vai ficar pulando de parte para parte.</p><h3>Por que Promises?</h3><p>Se já tínhamos uma implementação de funções assíncronas, por que houve a preocupação de criar todo um novo padrão para podermos ter exatamente a mesma coisa? A questão aqui é mais a organização do código do que a funcionalidade.</p><p>Imagine que temos uma função que lê um arquivo, após este arquivo ser lido ela precisa escrever em um outro arquivo e ai executar outra função assíncrona. Nosso código ficaria assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/48c0979cc6207d26da61fb26c01c5594/href">https://medium.com/media/48c0979cc6207d26da61fb26c01c5594/href</a></iframe><p>Veja que o código fica super complicado para ler… Isso é o que chamamos de <a href="http://callbackhell.com/"><em>callback hell</em></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FH4SxVM6nmatC18t.png" /><figcaption>Representação de um callback hell</figcaption></figure><p>As Promises foram um passo seguinte para que pudéssemos melhorar um pouco a execução do nosso código. Primeiramente vamos melhorar nosso código anterior, podemos extrair as funções posteriores para outros blocos, melhorando um pouco a nossa visualização:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5d8eeff3babab48a3e60aee2fb1fefb3/href">https://medium.com/media/5d8eeff3babab48a3e60aee2fb1fefb3/href</a></iframe><p>Agora o problema é outro, estamos encadeando nossas funções e fica muito difícil entender todo o fluxo porque temos que passar em várias partes do código. Com Promises, nosso código ficaria assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8af3e56d7ca7225031245499712cd478/href">https://medium.com/media/8af3e56d7ca7225031245499712cd478/href</a></iframe><p>Veja que agora, apesar de nosso código não ter reduzido muito em tamanho, ele está mais legível, porque temos a implementação do then , de forma que conseguimos ver todo o <em>pipeline</em> de execução.</p><h3>Promises</h3><p>Promises, como já dissemos, definem uma ação que vai ser executada no futuro, ou seja, ela pode ser resolvida (com sucesso) ou rejeitada (com erro).</p><blockquote>Há uma diferença entre <em>lançar um erro</em> e <em>rejeitar uma promise</em>. Lançar (dar um throw ) no erro, vai parar a execução do seu código, é o equivalente a darmos um return em uma função. Porém <strong>rejeitar</strong> uma Promise fará com que o código continue sendo executado posteriormente</blockquote><p>A anatomia de uma Promise segue a seguinte API:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e9bf58ce36ce8fe1ce949183367c0fa4/href">https://medium.com/media/e9bf58ce36ce8fe1ce949183367c0fa4/href</a></iframe><p>Como podemos ver, toda a Promise retorna um método then e outro catch , utilizamos o then para tratar quando queremos <strong>resolver</strong> a Promise, e o catch quando queremos tratar os erros de uma Promise <strong>rejeitada. </strong>Tanto then quanto catch retornam <strong>outra Promise</strong> e é isso que permite que façamos o encadeamento de then.then.then .</p><p>Para criarmos uma Promise é muito simples, basta inicializar um new Promise que recebe uma função como parâmetro, esta função tem a assinatura (resolve, reject) =&gt; {} , então podemos realizar nossas tarefas assíncronas no corpo desta função, quando queremos retornar o resultado final fazemos resolve(resultado) e quando queremos retornar um erro fazemos reject(erro) .</p><h4>Estados de uma Promise</h4><p>Uma Promise pode assumir quatro estados principais:</p><ul><li><em>Pending:</em> O estado inicial da Promise, ela foi iniciada mas ainda não foi realizada nem rejeitada</li><li><em>Fulfilled: </em>Sucesso da operação, é o que chamamos de uma Promise <strong>realizada</strong> (ou, em inglês, <em>resolved) — </em>eu, pessoalmente, prefiro o termo <strong>resolvida</strong>.</li><li><em>Rejected: </em>Falha da operação, é o que chamamos de uma Promise <strong>rejeitada </strong>(em inglês, <em>rejected</em>)</li><li><em>Settled:</em> É o estado final da Promise, quando ela já sabe se foi <em>resolved</em> ou <em>rejected</em></li></ul><p>Uma Promise que está pendente (<em>pending</em>) pode vir a se tornar uma Promise resolvida com um valor, ou então rejeitada por um motivo (que é o erro). Sempre que qualquer um dos dois casos acontecer, o método then da Promise será chamado e ele será o responsável por verificar se houve um erro ou um sucesso, chamando o método resolve em caso de sucesso, ou reject em caso de falha.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/801/0*j7fbuFM48PBuakha.png" /><figcaption>Diagrama de como uma Promise se comporta (Fonte: MDN)</figcaption></figure><blockquote>É importante mencionar que o método catch é somente um alias para o segundo parâmetro de then . Ele torna o código bem mais legível, então é considerado uma boa prática</blockquote><h4>Encadeamento</h4><p>O encadeamento de Promises (com then e catch ) é muito importante para entendermos o que está acontecendo, porque dependendo da forma como encadeamos nossas chamadas, vamos ter resultados diferentes.</p><p>Vamos tomar o <a href="http://bevacqua.github.io/promisees/?utm_content=buffer4155a&amp;utm_medium=social&amp;utm_source=twitter.com&amp;utm_campaign=buffer#code=const+p+%3D+new+Promise((resolve%2C+reject)+%3D%3E+%7B%0A++setTimeout(()+%3D%3E+resolve(&#39;yay&#39;)%2C+5000)%0A%7D)%0A%0A%2F%2F+Ambos+os+callbacks+est%C3%A3o+ligados+a+p%0Ap.then((res)+%3D%3E+%7B%7D%2C+(rej)+%3D%3E+%7B%7D)%0A%0A%2F%2F+Isto+aqui+%C3%A9+a+mesma+coisa%0Anew+Promise((resolve%2C+reject)+%3D%3E+%7B%7D)%0A++.then((res)+%3D%3E+%7B%7D%2C+(rej)+%3D%3E+%7B%7D)">seguinte exemplo</a>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b9cc04fc727d0be3288e42d384a631f0/href">https://medium.com/media/b9cc04fc727d0be3288e42d384a631f0/href</a></iframe><p>Quando fazemos isto, estamos ligando tanto o bloco then quanto o catch na mesma Promise p , perceba que estamos passando dois parâmetros para a função then— ou então diretamente na Promise criada no segundo caso, não há diferenças. Vamos ter este mapa:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/436/1*hbufJvPRLSxkR5dFTz75PA.png" /><figcaption>Ligando then e catch ao mesmo tempo</figcaption></figure><p>Vamos modificar um pouco o nosso código e fazer o bind do nosso then e catch separadamente:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/46adbaa35066f5ed1a6df9774910052b/href">https://medium.com/media/46adbaa35066f5ed1a6df9774910052b/href</a></iframe><p>Isso nos dá dois <em>bindings</em> diferentes para a mesma Promise, apesar da semântica ser um pouco diferente, o resultado ainda é o mesmo que temos acima, porém com duas ligações diferentes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/452/1*bfVJg7TYfvgD-Zh6vIKO7w.png" /><figcaption>Separando os bindings</figcaption></figure><p>Agora temos o terceiro caso, onde criamos o encadeamento de um catch no próprio then — isto porque, lembre-se, todo o then e catch retorna uma outra Promise para nós — vamos modificar o nosso código:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/929b870ec42032e104caa9064a2aeb8d/href">https://medium.com/media/929b870ec42032e104caa9064a2aeb8d/href</a></iframe><p>Isto fará com que a ligação do catch seja feita na Promise retornada por then e não na nossa Promise criada originalmente:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/652/1*TeWN5BOERkVjelEP_o9HJA.png" /><figcaption>Encadeamento de Promises</figcaption></figure><h4>Um catch para todos controlar</h4><p>Essencialmente, tudo o que fizemos acima não tem muitas diferenças práticas, isto por conta de algo super importante que não acontecia nos <em>callbacks</em>.</p><p>Quando falamos de <em>callbacks</em> temos que pensar em funções externas. Um callback poderia aceitar uma função única que iria receber como parâmetro um objeto err e um data , que são respectivamente os erros ocorridos na função assíncrona que o chamou e os dados recebidos no caso de sucesso (muito próximo do nosso then e catch ), porém esta função só iria capturar os erros <strong>daquela </strong>execução, ou seja, para cada <em>callback</em> teríamos que ter uma nova função de recuperação e de tratamento de erros ou então teríamos que tratar cada erro em uma função separada.</p><p>Com Promises isso não acontece, isto porque, independente do tratamento que damos a Promise, ele sempre vai buscar o primeiro tratamento de erros disponível, em outras palavras, todos os erros irão cair para o primeiro catch que encontrarem. Vamos a um exemplo.</p><p>Temos uma função cara ou coroa, ela vai resolver a promise com sucesso se o valor de Math.random() — que te dá um número aleatório entre 0 e 1 — for maior que 0.5, caso contrário ela vai rejeitar essa Promise:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4a819122e84a349cecedff7369e48a76/href">https://medium.com/media/4a819122e84a349cecedff7369e48a76/href</a></iframe><p>Colocamos um then e um catch simples, se for resolvida, vamos logar a mensagem no stdout se não, no stderr . Isso nos dá o seguinte mapa para um <strong>sucesso</strong> (quando tiramos um número maior que 0.5):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/1*qCtDBYVCrlVLllZUW4gLHQ.png" /><figcaption>Nossa função log é executada, mas não a error</figcaption></figure><p>E vamos ter printado no console somente yay . Porque o then foi chamado já que resolvemos a Promise. Mas no nosso mapa anterior podemos ver que temos um catch ligado nele, isto acontece porque ligamos o catch no then , então ele computou que haveria uma chamada de tratamento de erros, mas como não houve uma rejeição ela não foi executada. Se colocássemos p.then e depois p.catch, ao invés de p.then.catch, teríamos o seguinte mapa de sucesso:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/450/1*WJCHo3XkTXmZ8ApvZ0Ujzw.png" /><figcaption>O log está ligado somente à Promise</figcaption></figure><p>Veja que agora o catch não foi computado porque ele não está ligado no then , mas sim no p original. Da mesma forma em um erro teríamos somente o error() sendo executado:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/438/1*85DTGdW_w7CEbTrOXJoJFw.png" /><figcaption>O catch está ligado somente à Promise e não mais ao then</figcaption></figure><p>Agora o que acontece quando temos uma série de ações que queremos tomar depois? Por exemplo:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eafa598c7175d75fcdfe2b44d703d1a3/href">https://medium.com/media/eafa598c7175d75fcdfe2b44d703d1a3/href</a></iframe><p>Veja que aqui estamos executando 3 ações após a primeira Promise, a cada ação nós printamos na tela o que estamos fazendo e retornamos o mesmo valor para a próxima Promise — lembre-se que cada then retorna outra Promise, então todo o valor retornado dentro de um then é como se estivéssemos dando um resolve(valor) dentro de uma Promise — e por fim temos um handler de erro que deverá pegar todos os erros da primeira Promise e printar um no no console:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*81-Fn1UeAO7IwOPX1HV73A.png" /><figcaption>Fluxo de sucesso</figcaption></figure><p>Em um fluxo de sucesso vamos ter todas as ações executadas e o nosso catch contabilizado mas não executado, nossa saída seria algo assim:</p><pre>yay da ação 1<br>yay da ação 2<br>yay da ação 3</pre><p>E para um fluxo de erro teríamos:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/450/1*YPmJU3iq-am-eRzPJ0NlNw.png" /></figure><p>Com simplesmente um no no console, ou seja, ele pulou todos os then , e caiu diretamente no nosso handler de erro. O que acontece se colocamos um outro catch na jogada?</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a67abf7e61af4551e6def3b819fd981b/href">https://medium.com/media/a67abf7e61af4551e6def3b819fd981b/href</a></iframe><p>Veja que estamos declarando agora dois handlers de erro. O que deve acontecer é que, quando a Promise for rejeitada, ele deverá chamar o primeiro handler (erro1) e parar por ai, certo? Errado:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4cKVCn3RGzfIBVZKU4NTzg.png" /><figcaption>Cada catch só captura a primeira rejeição de uma Promise</figcaption></figure><p>O que aconteceu aqui? Nosso catch erro1 foi executado, mas parece que todo o resto do fluxo seguiu normalmente! Lembre-se que &quot;jogar&quot; um erro é diferente de rejeitar uma Promise. O throw irá parar a execução do sistema, mas um reject irá manter o sistema sendo executado, por esta razão é possível ter múltiplos catch em uma Promise. Cada catch irá capturar o erro relativo às Promises anteriores, uma vez capturado, o valor que ele retornar será passado para a próxima Promise que executará normalmente.</p><p>No caso acima vamos ter a seguinte saída no console:</p><pre>Primeiro catch<br>Error da ação 2<br>Error da ação 3</pre><p>E em um caso de sucesso vamos obter a mesma saída que já obtemos anteriormente, porque não vamos cair em nenhum bloco catch . Isso é importante porque muitos pensam que o catch é universal, mas na verdade, quando encadeados em outros then , o primeiro erro que acontece consome o primeiro catch e assim por diante.</p><p>Agora, se tivéssemos feito algo deste tipo:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/584c47c87a6574582b66eec1fda83a6d/href">https://medium.com/media/584c47c87a6574582b66eec1fda83a6d/href</a></iframe><p>Veja que estamos separando o que é sucesso do que é erro, então o nosso mapa de erros seria algo assim:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/494/1*_PCCi52kBfi3KHEqSeQVnQ.png" /></figure><p>E isso significa que iríamos printar ambos os erros no console:</p><pre>Primeiro catch<br>no</pre><p>Perceberam como a ordem do encadeamento importa? E neste caso:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/01d526e2f68bedd38ebb2f1d3699eafe/href">https://medium.com/media/01d526e2f68bedd38ebb2f1d3699eafe/href</a></iframe><p>Aqui vamos ter alguns casos específicos, se p falha, então a função erro1 e erro2 devem ser executadas, mas não erro3 de acordo com esse mapa:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vYbMpyBkvz4s5zJzDt37Ow.png" /></figure><p>Isto porque estamos criando um handler de erro acoplado <strong>à Promise original</strong> e outro que está acoplado ao resultado da execução posterior (os then ). Então nossa saída seria algo assim:</p><pre>Eu pego todos os erros dessa promise<br>Eu pego somente os erros que aconteceram até a ação 1<br>Eu executo normalmente<br>Eu executo normalmente</pre><p>Isto acontece pelo mesmo motivo que falamos antes, o primeiro catch é consumido e as demais funções executam normalmente. Agora, se a Promise p é resolvida, então sempre vamos ter um erro na acao1 — isto porque demos um throw dentro dela, e isso é o equivalente a dar um reject na Promise que este then retorna — e ai vamos ter outro mapa:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8OoAikDjSfGeUx4Tf2Ho8A.png" /></figure><p>Veja que erro1 não é executado de maneira nenhuma, porque a Promise p foi resolvida com sucesso, o que deu o erro foi uma de suas execuções posteriores, a acao1 , e o catch com a função erro1 não está ligado neste then . Então teríamos a seguinte saída no console:</p><pre>Estou rejeitando o valor, o catch a seguir deve tratar<br>Eu pego somente os erros que aconteceram até a ação 1<br>Eu executo normalmente<br>Eu executo normalmente</pre><p>Perceba que as demais Promises de acao2 e acao3 continuam executando em todos os casos.</p><h4>Promise.finally</h4><p>O ES9, lançado em 2018, trouxe uma nova funcionalidade às Promises, o finally . De acordo com a <a href="https://github.com/tc39/proposal-promise-finally">especificação</a>, este método <strong>sempre</strong> será executado, independente se a Promise foi resolvida ou rejeitada. Isto foi criado para manter a ideia do try/catch/finally que já existe há décadas em outras linguagens e pode ser muito útil em diversos casos.</p><p>Em um bloco padrão de try/catch/finally temos a seguinte estrutura:</p><pre>try {<br> // código executado<br>} catch (erro) {<br> // irá cair aqui se o código executado jogar um erro<br>} finally {<br> // essa parte sempre vai ser executada<br>}</pre><p>O mesmo funciona para as Promises. Vamos a um exemplo:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/589fecf4e04222c84343c54e82ddcae1/href">https://medium.com/media/589fecf4e04222c84343c54e82ddcae1/href</a></iframe><p>No caso de um sucesso, vamos ter a seguinte saída do console:</p><pre>yay<br>Eu sempre sou executado</pre><p>Já no caso de um erro:</p><pre>no<br>Eu sempre sou executado</pre><p>Ou seja, é como se tivéssemos sempre alguém ouvindo a finalização de nossas Promises para <strong>sempre</strong> executar um trecho de código. O método finally já está disponível desde a versão 10.3 do <a href="https://node.green/#ES2018-features-Promise-prototype-finally">NodeJS</a> e <a href="https://caniuse.com/#feat=promise-finally">na maioria dos browsers</a>.</p><h4>Settled</h4><p>Um estado importante de comentarmos aqui é o estado <em>Settled</em> de uma Promise. Como já falamos antes, este estado é quando temos uma Promise completamente resolvida, que já recebeu seus valores de <em>resolved</em> ou <em>reject, </em>ou seja, é uma Promise que já &quot;acabou&quot;.</p><p>Uma Promise neste estado já teve seus handlers then e/ou catch executados. A partir deste ponto dizemos que ela está terminada, agora, se no futuro adicionarmos outro handler, digamos, outro then , na mesma Promise, o que acontece?</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cfa0aca8f3294348eaa647d9ce3bc808/href">https://medium.com/media/cfa0aca8f3294348eaa647d9ce3bc808/href</a></iframe><p>Vamos analisar o fluxo desta Promise:</p><ol><li>A Promise é criada</li><li>O handler then é adicionado</li><li>Após 2s a Promise recebe a resposta do resolve</li><li>A Promise executa o handler e é dada como <em>settled</em></li><li>Um novo handler é adicionado</li></ol><p>Promises que já estão com o estado definido como <em>settled</em> são resolvidas imediatamente após a adição posterior de um novo handler, ou seja, nosso handler tardio de multiplicação vai retornar <strong>na hora</strong> o valor 2000:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/270/1*iF-gyQ7ikjtH5GJyAI10FQ.gif" /><figcaption>Veja a segunda execução retornando no mesmo momento</figcaption></figure><h3>Promises de Promises</h3><p>É possível para que uma Promise retorne outra Promise para ser resolvida, por exemplo, vamos imaginar que temos que pegar duas informações diferentes de APIs diferentes, mas uma depende da outra.</p><p>Quando retornamos uma Promise para outra Promise, só vamos ter a resolução completa do conjunto quando ambas as Promises entrarem no estado <em>settled. </em>Ou seja, se, dentro de uma Promise, chamamos outra Promise, a primeira só se resolverá após o retorno da segunda como <em>settled:</em></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/66da6e9319519caf3b11a4a3fe09e1aa/href">https://medium.com/media/66da6e9319519caf3b11a4a3fe09e1aa/href</a></iframe><p>O que acontece aqui é a Promise inteira só será resolvida após a execução do primeiro fetch e também do segundo fetch , que retorna uma Promise resolvida no último then . Vamos a outro exemplo mais simples.</p><p>Uma Promise que retorna outra Promise que pode ou não ser resolvida após 1s, usando nossa mesma função de moeda:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5b7174d6454cfb2197f947afeccb599e/href">https://medium.com/media/5b7174d6454cfb2197f947afeccb599e/href</a></iframe><p>Veja como fica esta execução em tempo real:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/370/1*WlOqIXXv24t1mjfBQmlFMw.gif" /><figcaption>Bloqueio de Promises</figcaption></figure><p>Veja que o primeiro then fica amarelado, porque ele está esperando a segunda Promise (a que tem o setTimeout ) ser resolvido, isto significa que ele está <strong>bloqueado</strong>, na espera da segunda Promise. Quando a mesma retorna, todos os outros handlers são resolvidos instantaneamente.</p><h3>Métodos de Promises</h3><p>Além do then , catch e finally uma Promise também possui outros métodos estáticos muito úteis.</p><h4>Promise.resolve e Promise.reject</h4><p>Estes dois métodos são atalhos para quando queremos retornar uma Promise que sempre terá o mesmo valor, ou sempre resolvida, ou sempre rejeitada, de forma que não precisamos ficar criando todo o boilerplate de new Promise... .</p><p>Vamos imaginar que temos a seguinte Promise:</p><pre>const p = new Promise((resolve) =&gt; resolve(1056))</pre><p>Independente do que acontecer, a Promise <strong>sempre</strong> resolverá para o valor 1056. Ela nunca terá um catch e nunca lançará um erro… Então podemos simplesmente escrever assim:</p><pre>const p = Promise.resolve(1056)</pre><p>De forma similar podemos fazer com o reject :</p><pre>const p = Promise.reject(&#39;Erro&#39;)</pre><h4>Promise.all</h4><p>A ideia do método all é executar ações simultaneamente, ou seja, disparar uma série de Promises ao mesmo tempo e esperar pelo retorno de todas elas. Isto é muito útil quando, por exemplo, temos que pegar informações de várias APIs que não são relacionadas entre si.</p><blockquote>Um exemplo mais real seria: &quot;Buscar todos os dados de redes sociais de um usuário&quot;, podemos pegar todos os usuários desse cliente nas redes sociais e disparar um monte de Promises, uma para cada rede social e esperar a resposta.</blockquote><p>O método Promise.all é justamente isso. Ele recebe um Array de Promises não resolvidas e inicia todas elas. Ele só vai terminar em dois casos:</p><ol><li>Todas as Promises do array foram resolvidas</li><li>Pelo menos uma Promise foi rejeitada</li></ol><p>Ou seja, é um método tudo ou nada, se todas as Promises tiverem sucesso, o método terá sucesso, porém no primeiro erro, o método te devolverá um erro.</p><p>Vamos ver este snippet de código (também presente no <a href="http://bevacqua.github.io/promisees/?utm_content=buffer4155a&amp;utm_medium=social&amp;utm_source=twitter.com&amp;utm_campaign=buffer#code=Promise.all(%5B%0A++new+Promise(resolve+%3D%3E+setTimeout(resolve%2C+1500))%2C%0A++new+Promise(resolve+%3D%3E+setTimeout(resolve%2C+900))%2C%0A++new+Promise(resolve+%3D%3E+setTimeout(resolve%2C+2200))%0A%5D)%0A.then(results+%3D%3E+results.length.b.c)%0A.then(c+%3D%3E+console.info(c))%0A.catch(err+%3D%3E+console.error(err))">Promisees</a>):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/edbfc25e8cd6891d3a211b8aae09ed89/href">https://medium.com/media/edbfc25e8cd6891d3a211b8aae09ed89/href</a></iframe><p>Criamos um array de várias Promises, cada uma delas resolve em um momento diferente, porém nenhuma delas tem uma propriedade b ou c então elas serão rejeitadas naturalmente, veja a animação:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*tA77H8N5tqeGn43RZ0Esdw.gif" /><figcaption>Ao ser rejeitado, todo o método é finalizado</figcaption></figure><p>Perceba que temos 3 new() ligados a um [all] , eles se resolvem em momentos diferentes, uma vez que <strong>todos </strong>são resolvidos, o método then é chamado, mas ele retorna um erro que rejeita a sequencia de Promises, nesse instante todo o método é finalizado e o array de Promises é dado como <em>settled</em>. Retornando o resultado do erro.</p><p>Vamos modificar o código para que elas passem:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/081a2f94c269f50b9c9144d8ac350a3d/href">https://medium.com/media/081a2f94c269f50b9c9144d8ac350a3d/href</a></iframe><p>Veja como o mapa fica agora:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*pqpq8lDeNNPdIcAFlvEipA.gif" /></figure><p>Agora é bem mais claro de ver que o [all] espera todas as Promises se resolverem antes de chamar seu handler, no caso de sucesso, o Promise.all retorna um array com todos os resultados das Promises enviadas.</p><p>Vamos ver o que acontece no caso de uma dessas Promises ser rejeitada:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3c8f094ac7ecdb7257364fdc03f135ca/href">https://medium.com/media/3c8f094ac7ecdb7257364fdc03f135ca/href</a></iframe><p>Perceba como podemos ver exatamente o funcionamento do Promise.all :</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*4uRl2gq-Z_YYxmf9W65eIA.gif" /></figure><p>Quando a segunda Promise é rejeitada, todos os handlers são chamados imediatamente, ou seja, o método retorna o valor do erro da segunda Promise para o catch e ignora completamente a última Promise, ela ainda é executada, mas não tem seu valor consumido por ninguém.</p><h4>Promise.race</h4><p>O método Promise.race faz exatamente o que o nome diz, ele recebe um array de Promises, inicia todas elas, a que retornar Primeiro vai ser o retorno do método por completo. Ele é um caso especial do Promise.all onde, ao invés de esperar <strong>todas</strong> as Promises serem resolvidas, simplesmente retorna o primeiro resultado que obtiver. Veja este exemplo:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7e5277f13d61fc8dbaff87d78091ec4e/href">https://medium.com/media/7e5277f13d61fc8dbaff87d78091ec4e/href</a></iframe><p>Temos dois arrays de Promises, um deles é resolvido em 4s e depois rejeitado em 8s, enquanto o outro é rejeitado em 2s e depois resolvido em 6s e 10s, vamos ver o mapa:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/470/1*9V3HBMUAxM1DwQrqMp8tpg.gif" /></figure><p>Perceba que, no primeiro caso, o método [race] aguarda duas Promises, enquanto o segundo aguarda 3. O primeiro tem sua Promise resolvida e ele já se torna verde, porque é o resultado que ele espera, então a segunda Promise (que é rejeitada) não é sequer consumida.</p><p>No segundo race (o que fica vermelho no final), temos uma Promise que é rejeitada logo de cara, então todas as demais Promises são ignoradas e o handler catch é chamado.</p><h3>Async/Await</h3><blockquote>Não irei me estender demais sobre async e await por aqui porque já escrevi um <a href="https://imasters.com.br/desenvolvimento/funcoes-assincronas-e-retornos-como-o-async-await-tornaram-o-codigo-mais-legivel">artigo</a> que fala sobre ele com mais detalhes. O que vou dizer aqui é somente uma desmistificação do que a maioria pensa sobre estas keywords.</blockquote><p>Async e await são keywords que foram introduzidas no ES8 em 2017. Basicamente é um <em>syntax sugar</em> (uma firula de linguagem que foi adicionada somente para poder facilitar a escrita) do then e catch .</p><p>O motivo pela adição do async/await foi o mesmo da adição das Promises no JavaScript, o callback hell. Só que dessa vez tínhamos o Promise hell, onde ficávamos aninhando Promises dentro de Promises eternamente e isso tornava tudo muito mais difícil de se ler.</p><p>A proposta de funções assíncronas é justamente nivelar todo mundo em um único nível. Escrever um código assim:</p><pre>async function foo () {<br> if (Math.random() &gt; 0.5) return &#39;yeah&#39;<br> throw new Error(&#39;ops&#39;)<br>}</pre><p>É a mesma coisa que escrever isso:</p><pre>const foo = new Promise((resolve, reject) =&gt; {<br> if (Math.random() &gt; 0.5) return resolve(&#39;yeah&#39;)<br> reject(&#39;ops&#39;)<br>})</pre><p>A diferença é que podemos deixar tudo no mesmo nível, ao invés de escrevermos:</p><pre>foo.then((resposta) =&gt; { ... }).catch((erro) =&gt; ...)</pre><p>Podemos fazer isto (desde que estejamos dentro de outra função async):</p><pre>async function bar () {<br> try {<br>    const resposta = await foo()<br> } catch (erro) { throw erro }</pre><h3>Futuras implementações</h3><p>O JavaScript é um padrão que está em constante mudança. Portanto já existem novas ideias e implementações para novos métodos de Promises, o mais legal é o allSettled .</p><h4>Promise.allSettled</h4><p>Este método veio para sanar um grande problema com o Promise.all . Em muitos casos reais, nós queremos executar várias Promises de forma paralela e trazer o resultado de <strong>todas</strong> elas, e não só o erro ou então só o array de sucessos, nós queremos tanto os erros, quanto os sucessos.</p><blockquote><strong>Atualização: </strong><em>A proposta do </em>allSettled<em> foi implementada na versão 12 do Node.js e já está disponível para uso nas últimas versões LTS a partir da 12 (14 e 16), obrigado ao </em><a href="https://github.com/KuryKat"><em>@KuryKat</em></a><em> pelo aviso: </em><a href="https://node.green/#ES2020-features--Promise-allSettled">https://node.green/#ES2020-features--Promise-allSettled</a></blockquote><p>Vejamos o exemplo — que está também na documentação — sobre o motivo desta proposta:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/99dcb9804cdfc6e93c097cb5a9860c17/href">https://medium.com/media/99dcb9804cdfc6e93c097cb5a9860c17/href</a></iframe><p>Este é um problema comum com o Promise.all , quando queremos pegar o resultado de todas as Promises, temos que fazer uma função de reflexão, que nada mais faz do que atribuir um handler para cada uma das Promises no array e jogar isso tudo dentro do all . Desta forma estamos sobrescrevendo o comportamento original da Promise pelo nosso próprio e retornando para cada valor um objeto com as descrições do que aconteceu.</p><p>A proposta pretende criar um método allSettled para abstrair a função reflect :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2651d484a82a6bf34ccadd831ab40cbc/href">https://medium.com/media/2651d484a82a6bf34ccadd831ab40cbc/href</a></iframe><p>Ambos os casos irão nos dar um array de objetos no final com esta assinatura:</p><pre>[<br>  { status: &#39;resolved&#39;, value: &#39;valor da resolução&#39; },<br>  { status: &#39;rejected&#39;, reason: &#39;mensagem de erro&#39; }<br>]</pre><p>Para mais informações veja a <a href="https://github.com/tc39/proposal-promise-allSettled">página da proposta</a>.</p><h3>Conclusão</h3><p>O Objetivo da escrita deste artigo não foi somente para entrar mais a fundo em Promises como um todo, mas sim devido a uma grande dificuldade que notei em vários programadores (até mesmo experientes, incluindo eu mesmo) com o fluxo assíncrono do JavaScript.</p><p>Não deixe de acompanhar mais do meu conteúdo no <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=medium_post">meu blog</a> e se inscreva na <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=medium_post">newsletter</a> para receber notícias semanais!</p><p>Espero que, com este artigo, possamos entender de uma vez por todas o que são Promises e o que elas significam e qual é a importância desta adição para a linguagem e porque todos deveriam saber Promises ao invés de callbacks.</p><p><strong>Veja a segunda parte deste artigo </strong><a href="https://dev.to/khaosdoctor/construindo-uma-promise-do-zero-4ndp"><strong>aqui!</strong></a></p><blockquote>Se você viu algum erro, tem alguma sugestão ou quer adicionar algo no artigo, fala comigo através de qualquer uma das minhas redes sociais em <a href="http://lsantos.dev">http://lsantos.dev</a> :D</blockquote><h4><strong>Edição 09/05/2019</strong></h4><p>Para complementar este artigo, fui chamado pela Digital Innovation One para fazer um webinar sobre Promises, onde me baseei no que aprendemos aqui e mostrei de forma prática! Recomendo muito para acompanhar e acrescentar ao estudo:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FwZwMVbgQZps%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DwZwMVbgQZps&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FwZwMVbgQZps%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/9586e86ff8b12f95946a022ef8545294/href">https://medium.com/media/9586e86ff8b12f95946a022ef8545294/href</a></iframe><h4>Referências</h4><ul><li><a href="https://github.com/tc39/proposal-promise-allSettled">https://github.com/tc39/proposal-promise-allSettled</a></li><li><a href="https://braziljs.org/blog/promises-no-javascript/">https://braziljs.org/blog/promises-no-javascript/</a></li><li><a href="https://en.wikipedia.org/wiki/Futures_and_promises">https://en.wikipedia.org/wiki/Futures_and_promises</a></li><li><a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise">https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Run-to-completion">https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Run-to-completion</a></li><li><a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Usando_promises">https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Usando_promises</a></li><li><a href="https://ponyfoo.com/articles/es6-promises-in-depth">https://ponyfoo.com/articles/es6-promises-in-depth</a></li><li><a href="https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original">https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original</a></li><li><a href="https://medium.freecodecamp.org/es9-javascripts-state-of-art-in-2018-9a350643f29c">https://medium.freecodecamp.org/es9-javascripts-state-of-art-in-2018-9a350643f29c</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=32442ec725c2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/trainingcenter/entendendo-promises-de-uma-vez-por-todas-32442ec725c2">Entendendo Promises de uma vez por todas</a> was originally published in <a href="https://medium.com/trainingcenter">Training Center</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como é trabalhar como desenvolvedor backend, por Luiz Felipe Limeira]]></title>
            <link>https://medium.com/trainingcenter/como-%C3%A9-trabalhar-como-desenvolvedor-backend-por-luiz-felipe-limeira-f795edc4fe48?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/f795edc4fe48</guid>
            <category><![CDATA[training-center]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[entrevista]]></category>
            <category><![CDATA[profissão]]></category>
            <category><![CDATA[developer]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Wed, 21 Nov 2018 13:31:01 GMT</pubDate>
            <atom:updated>2018-11-21T13:31:01.891Z</atom:updated>
            <content:encoded><![CDATA[<p>Esse post é parte de uma série de entrevistas para o <a href="https://trainingcenter.io/">Training Center</a> sobre o que um(a) profissional pode dizer a respeito da sua área de atuação visando mostrar para outras pessoas como é trabalhar no que fazem, esclarecendo para algumas pessoas se elas se dariam bem trabalhando na área ou mesmo só para mostrar para outras pessoas como é trabalhar com isso.</p><p>O entrevistado desta vez será um dos co-organizadores do <a href="https://www.meetup.com/Training-Center-SP/">meetup do Training Center em SP</a>, Luiz Felipe Limeira!</p><h3>Um pouco sobre você…</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CYnIBlLXkZdYXYZeXmJ1gg.jpeg" /><figcaption>Luiz Felipe Limeira</figcaption></figure><p>Sou formado em Ciências da Computação desde 2015 e trabalho como desenvolvedor desde 2011, atualmente trabalho no <a href="http://pagar.me/">Pagar.me</a> utilizando Node.js no desenvolvimento de serviços voltados para pagamentos de mundo físico. Fora do âmbito profissional, eu tenho prezado bastante por minha qualidade de vida, então eu tento me manter longe de computadores e fazer coisas que eu gosto, como andar de patins, longboard, viajar, ver séries no Netflix e outros, mas também reservo tempo para frequentar meetups, escrever artigos e ajudar na organização do meetup do Training Center em SP.</p><h4>Como você conheceu a área que você trabalha hoje?</h4><p>Se eu me lembro bem, tudo começou quando meu primo começou a fazer faculdade de design gráfico e me mostrava uns códigos estranhos que serviam para fazer sites.</p><p>Não lembro ao certo quantos anos eu tinha, mas com mais ou menos 15 anos, apareceu um trabalho de história que o professor pediu para colocar em um blog ou site, e o link para todos os trabalhos deveriam ficar centralizados em um “portal”, eu resolvi ficar responsável por fazer essa página para a sala e lembrei dos códigos estranhos. Com o Google como meu guia eu fui desenvolvendo a página com um monte de imagens de fundo e os links (rsrsrs), como podem imaginar, ficou bem feia.</p><p>O trabalho não foi para o ar, pois trocaram o professor de história, e obviamente ele achou melhor não publicar os trabalhos por conta de direitos de imagem e autorais (quem nunca copiou texto da internet para trabalhos escolares?). Esse foi meu primeiro contato com desenvolvimento, o que me ajudou quando eu entrei na ETEC para cursar Informática para Internet.</p><h4>Depois disso você continuou seus estudos na área? Como foi todo o processo para, de fato, conseguir o primeiro trampo e quais dicas você daria para uma pessoa iniciante?</h4><p>Sim… Depois disso, eu gostei de ter feito o site, e tinha muita vontade de fazer Ciências da Computação, mas meu desempenho em matemática no colégio me fazia ter muitas incertezas, resolvi que não teria muita capacidade para esse curso, e no final do primeiro ano do colegial eu resolvi que queria fazer ETEC de Informática para Internet, que seria para eu ter uma base se era aquilo mesmo que eu iria fazer.</p><p>Durante o curso, um professor chamado Pirola, chamou todos para o auditório, como tínhamos dificuldades na parte de exatas do curso — e, consequentemente, na matéria dele — Ele explicou a importância de exatas para a área e as coisas que eram possíveis de serem feitas. Aquela palestra mudou minha vida, dei mais foco para exatas, minhas notas melhoraram na escola e resolvi que eu era sim capaz de fazer Ciências da Computação.</p><p>Na ETEC não me dei muito bem nas matérias de design e front-end e esse foi o sinal definitivo de que eu queria focar em ser desenvolvedor back-end (obs: na época o front-end era muito ligado a parte de design, usava muito flash também, hoje em dia eu gosto de mexer com front-end).</p><blockquote>Aquela palestra mudou minha vida, dei mais foco para exatas, minhas notas melhoraram na escola e resolvi que eu era sim capaz de fazer Ciências da Computação — Luiz Felipe</blockquote><p>Assim que eu terminei a ETEC e o ensino médio, não consegui pagar a faculdade logo de cara e tentei em algumas faculdades públicas, mas não passei, não tinha trabalho na época dos vestibulares, em janeiro eu recebi uma ligação de que o tio de uma amiga precisava de um web designer — esse foi meu primeiro emprego — trabalhava para pagar cursos de desenvolvimento com meus pais me ajudando a pagar metade, felizmente pude contar com a ajuda deles, pois, na época, ganhava pouco trabalhando como auxiliar de web design, até hoje não conheci o web designer da empresa rs (agora é engraçado relembrar, mas era triste).</p><p>Depois de um ano apenas trabalhando e fazendo cursos, aí sim seria possível começar a faculdade, e assim foi, com meu pai pagando metade e eu a outra metade até o terceiro ano da faculdade, depois de trocar de emprego duas vezes, eu já conseguia pagar a minha faculdade sozinho do terceiro ano em diante. As matérias exatas deram trabalho, mas consegui superá-las sem maiores traumas rs.</p><blockquote>Conheça pessoas, não se limite ao conhecimento que você vivencia no seu serviço ou escola — Luiz Felipe</blockquote><p>Acredito que grande parte do que eu consegui, foi por ter amigos… Meu primeiro emprego foi por indicação, o segundo foi por indicação de um amigo da faculdade… Uma dica que posso deixar quanto a isso é, conheça pessoas, não se limite ao conhecimento que você vivencia no seu serviço ou escola, pesquise muito no Google (ele foi o meu maior companheiro durante toda a minha caminhada) e algo que eu aprendi recentemente: não tenha medo de perguntar e errar, você sempre vai precisar aprender, e errar faz parte do processo, por isso erre. E você precisará saber como fazer da forma correta após o erro, então pergunte (para amigos, colegas de trabalho, Google, não importa quem ou o que, mas pergunte), pois assim você irá aprender. Essas são as minhas dicas.</p><blockquote>Não tenha medo de perguntar e errar, você sempre vai precisar aprender, e errar faz parte do processo — Luiz Felipe</blockquote><h4>Você disse que uma das coisas mais importantes é conhecer pessoas. Quais seriam suas dicas para encontrar e conhecer mais pessoas?</h4><p>Bom, acho que o principal é a pessoa estar aberta a conhecer novas pessoas, não ter medo de ir falar com alguém, e isso me travou um pouco quando comecei a ir em eventos e meetups, grande parte das pessoas que eu conheci nesses 2 últimos anos foi por conta das comunidades de desenvolvimento; Na minha opinião, esse é o melhor fruto que levamos dos eventos, mas temos que tomar cuidado para não sermos pessoas egocêntricas, estarmos sempre abertos a adquirir conhecimento e filtrar o mesmo, por que desenvolvedor por si só já é egocêntrico, nós queremos valorizar nosso conhecimento e muitas vezes o ego fala mais alto, isso é comum em comunidades de desenvolvimento.</p><blockquote>O principal é a pessoa estar aberta a conhecer novas pessoas, não ter medo de ir falar com alguém — Luiz Felipe</blockquote><p>Acho que, nesse caso, a empatia é a melhor coisa. Não responder algo como se fosse muito óbvio (o que é muito comum) é fácil, pois todos já tivemos dificuldades que pareciam fáceis para pessoas mais experientes.</p><h4>Na sua opinião o que seria a comunidade ideal? O ambiente ideal de cooperação, e como podemos chegar lá?</h4><p>Acho que uma comunidade em que as pessoas colocam mais a mão na massa, programação é prática, as empresas pedem experiência, comunidade é o lugar perfeito para dar isso para as pessoas.</p><p>Outro ponto é ter empatia e menos ego, as pessoas esquecem que já foram iniciantes, e que algumas pessoas têm mais dificuldade que as outras em algumas coisas, já vi muitas respostas ruins, já vi muitas respostas boas, acho que o primeiro passo é as pessoas deixarem de se colocar a cima das causas apenas para alimentarem seus egos ou se beneficiarem.</p><p>Para mim essas pessoas só alimentam mais e mais a síndrome do impostor das outras pessoas. Se explicarmos as dúvidas de forma fácil, sem valorizar ou querer mostrar que sabemos falar bonito, fica muito mais fácil atingir o objetivo principal, ajudar uns aos outros. Ou pelo menos esse deveria ser o motivo principal.</p><h4>Você participa de muitas comunidades, isso é um fato, qual foi o momento ou a ação mais importante que aconteceu contigo durante toda essa participação?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/522/1*XT58ZMCHA5YVkbxV2IdC4g.jpeg" /></figure><p>Acho que o mais marcante foi quando eu comecei a participar de comunidades, foi um choque, não entendia mais da metade das coisas que eu ouvia, me sentia muito atrasado, senti que todos os anos anteriores eu estava parado, e isso vendo agora, foi bem ruim para minha saúde, mas me fez evoluir muito e me fez correr atrás das coisas.</p><p>Mas o momento mais importante foi quando eu percebi como a tecnologia tem um poder real de mudar a vida das pessoas, como a tecnologia consegue prover a chance das pessoas mudarem o meio em que elas vivem e ultrapassarem as próprias barreiras, nessa caminhada já vi várias pessoas que provaram isso, e o projeto Juntos na TI foi um marco forte que me fez ter certeza disso.</p><h4>Você comentou sobre ansiedade e síndrome do impostor. Na sua opinião, esses seriam os piores problemas de desenvolvedores atuais? Quais são suas dicas para lidar com estes problemas?</h4><p>Na minha opinião esses, juntamente com a questão do ego e falta de empatia, são os maiores problemas, todos esses pontos juntos tornam o ambiente para quem está começando muito nocivo. Temos que parar de dizer o que as pessoas devem ou não fazer ou usar, e começar a falar sobre soluções, deixar de criar regras, e começar a deixar livre para a galera testar, errar e aprender. Esse é o ciclo do aprendizado.</p><blockquote>Temos que parar de dizer o que as pessoas devem ou não fazer ou usar, e começar a falar sobre soluções — Luiz Felipe</blockquote><p>Quando você cria um ambiente de disputa de ego, quem está observando acaba vendo muita coisa que é passada como o certo e não sabe para onde ir, assim surge a ansiedade e síndrome do impostor. Porque você está estudando HTML e CSS, aí alguém fala que você precisa aprender Angular, aí alguém fala que angular não presta e que você tem que aprender React, isso é muito prejudicial e as pessoas acabam não respeitando o tempo de aprendizado. Dessa forma as pessoas acham que precisam ter o equivalente a 1 ano de experiência em 1 mês.</p><p>Para mim está tudo atrelado…</p><h4>Qual seria o ambiente de trabalho ideal para você?</h4><p>O ambiente de trabalho ideal para mim é aquele em que todos são ouvidos, aquele em que existe respeito e oportunidade para todos crescerem, e principalmente que exista equilíbrio entre sua vida profissional e pessoal, não adianta ser o melhor lugar mas não conseguir pagar suas contas. Ame o seu trabalho e onde trabalha, mas não trabalhe <strong>apenas</strong> por amor.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DSZVq7Xl_l6sBZC3gL5Xqg.jpeg" /></figure><p>Para mim, o ambiente de trabalho envolve tudo isso, porque se você não está bem na sua vida pessoal, dificilmente vai conseguir agregar para que o ambiente profissional esteja bom.</p><p>Vale deixar claro que também não da para trabalhar apenas por dinheiro sem pensar no ambiente profissional no qual está inserido, pois esse ambiente pode ser ruim o suficiente para influenciar na sua vida pessoal. Coisas como estresse, desrespeito, falta de reconhecimento e falta de espaço para se expressar, podem facilmente influenciar suas relações na vida pessoal.</p><p>Acho que nossa área permite achar essa balança. Existe os dois extremos, mas existe bastante lugar (principalmente nos grandes polos tecnológicos) que permite essa balança para todos os níveis de profissionais.</p><h4>E quais são as skills necessárias para um profissional da sua área?</h4><p>Acho que falar skills necessárias seria criar uma regra desnecessária, a área é tão ampla e temos tantas pessoas que conseguiram de diferentes formas, o básico é gostar do que faz e correr atrás, de resto, até soft skills básicas como por exemplo trabalhar em equipe, eu já vi pessoas que não tinham e mesmo assim estão na área.</p><p>Acredito que para progredir na área é importante saber se vender, não ter medo de falar e mostrar o que sabe, ter soft skills como trabalho em equipe, uma comunicação não violenta, saber dar (e receber) feedbacks e não aceitar as coisas como estão, sempre pensar em melhorar.</p><blockquote>Acredito que para progredir na área é importante saber se vender, não ter medo de falar e mostrar o que sabe — Luiz Felipe</blockquote><p>Como desenvolvedor, é interessante ter a base, pois tem devs que sabem muitas ferramentas e frameworks, mas encontram dificuldades em algum momento com algoritmos, lógica de programação, dependendo da linguagem pode ter dificuldade com conceitos de OO, ou outros paradigmas.</p><p>Na minha opinião isso vai muito de encontro com “não pular steps”, respeitar o tempo de aprendizado. Lógico, nem todo mundo tem esse tempo ou condições financeiras para seguir passo a passo, por isso é importante recuperar isso em algum momento.</p><h4>E quais são os principais desafios e recompensas dessa área?</h4><p>Para mim os desafios mais legais ultimamente é a parte de negócio, não tem como você pensar em melhorar seu código e seu software, sem pensar no negócio, quando eu comecei a ligar mais e entender melhor sobre o assunto minha cabeça mudou, acho que isso é legal no desenvolvimento, nossa cabeça tem que estar sempre mudando e se adaptando.</p><p>É uma vida de estudos, é difícil parar, mas sempre lembrando que dá para estudar e aprender coisas novas sem prejudicar a saúde, aprender as coisas no devido tempo.</p><p>As principais recompensas, na minha opinião, são os produtos e os projetos que podemos fazer, ter a sensação de realmente estar fazendo algo e mudando as coisas; Outro ponto é que essa área permite uma liberdade incrível, tenho visto muitas pessoas mudando a vida delas, mudando de país, mudando o status social, a tecnologia é uma ponte e uma ferramenta poderosa para pessoas de baixa renda melhorarem de vida.</p><h4>Você pensa em mudar de área? Se não, por que um profissional deveria se tornar um Dev Backend?</h4><p>Nunca pensei em mudar de área, já pensei se no futuro eu pretendo mudar de responsabilidades, pensando na questão de carreira em Y, virar um <em>tech lead</em> ou virar um especialista, mas não em um futuro próximo, quero me aperfeiçoar em alguns aspectos para depois pensar nisso. Me vejo programando durante toda minha carreira, talvez menos ou mais, mas ainda sim programando.</p><blockquote>Me vejo programando durante toda minha carreira — Luiz Felipe</blockquote><p>Quanto a questão de ser desenvolvedor back-end, eu gosto muito dessa função, onde eu trabalho atualmente existem diversas funções que podemos desempenhar e uma fácil migração para elas, lá possui desenvolvimento back-end, front-end, mobile, baixo nível e outros, então se surgir alguma curiosidade, necessidade ou fizer sentido para o produto, me vejo explorando novas tecnologias e responsabilidades sim, acho interessante essas oportunidades.</p><p>Seguindo nessa linha, eu não acho que um(a) profissional deveria se tornar um(a) dev back-end, acho que um(a) dev deve procurar a função que mais lhe agrada e proporciona uma satisfação profissional, talvez se permitir conhecer novas coisas e então escolher, oportunidade para todas as áreas que eu citei não faltam.</p><p>Estudando e evoluindo fica bem tranquilo essa questão de experimentar novas responsabilidades, eu por exemplo não me sinto muito a vontade trabalhando como desenvolvedor front-end no momento pois sinto que não tenho as skills necessárias agora.</p><h3>Finalizando…</h3><p>Se você gostou desse post <a href="https://medium.com/trainingcenter/tagged/entrevista">temos uma sessão no blog só pra eles</a>, não esquece de dar um like e compartilhar! :D</p><p>Siga o Training Center no Twitter para se manter atualizado(a) sobre nossas novidades em <a href="http://twitter.com/trainingcentr">@trainingcentr</a>, Se quiser entrar em contato com o Luiz, veja o <a href="https://twitter.com/lflimeira02">Twitter</a> dele também!</p><p>Entre no nosso <a href="https://medium.com/trainingcenter/como-se-comportar-no-slack-do-training-center-a3715fb7c00f">Slack</a> para participar das decisões desse projeto e também para ver gifs engraçados! Conheça nossas iniciativas.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f795edc4fe48" width="1" height="1" alt=""><hr><p><a href="https://medium.com/trainingcenter/como-%C3%A9-trabalhar-como-desenvolvedor-backend-por-luiz-felipe-limeira-f795edc4fe48">Como é trabalhar como desenvolvedor backend, por Luiz Felipe Limeira</a> was originally published in <a href="https://medium.com/trainingcenter">Training Center</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Javascript Symbols: Decifrando o mistério]]></title>
            <link>https://medium.com/trainingcenter/javascript-symbols-decifrando-o-mist%C3%A9rio-383e359e64e3?source=rss-84c42a22cef7------2</link>
            <guid isPermaLink="false">https://medium.com/p/383e359e64e3</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[es6]]></category>
            <category><![CDATA[symbols]]></category>
            <category><![CDATA[iniciante]]></category>
            <dc:creator><![CDATA[Lucas Santos]]></dc:creator>
            <pubDate>Tue, 29 May 2018 14:21:01 GMT</pubDate>
            <atom:updated>2020-07-31T17:49:32.644Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EmXacb8uta4dY0t7.jpg" /></figure><p>Depois de algum tempo fora do ar, volto para apresentar a vocês um <strong>mistério</strong>, na verdade, nem tanto mistério, mas um tipo primitivo do Javascript que já existe há um tempo, mas nunca ficamos sabendo deles. Os <a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Symbol"><strong>Symbols</strong></a>.</p><h3>Symbols? É de comer?</h3><p>Symbols são um tipo primitivo do Javascript desde o ES6, mas eles já existiam antes disso. Symbols são usados para definir valores chaves para o funcionamento da linguagem há muito tempo, mas eles não eram acessíveis por ninguém, somente pelo próprio runtime e pelo interpretador. O que mudou foi que o ES6 tornou possível que nós, meros programadores, pudéssemos acessar esses tipos.</p><p>Symbols são caracterizados como uma espécie de &quot;metaprogramação&quot;, que foca principalmente em poder acessar aspectos do core da linguagem. Podemos criar um Symbol dessa forma:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a44c87ef4e84a5b2405e0c9b279e9678/href">https://medium.com/media/a44c87ef4e84a5b2405e0c9b279e9678/href</a></iframe><p>Perceba que não usamos nenhuma <em>keyword</em> new para definir um Symbol. Isto porque eles não são instâncias de nada, na verdade, se tentarmos fazer algo deste tipo:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d45d59ff458e546dee8b0f2a966b6ce1/href">https://medium.com/media/d45d59ff458e546dee8b0f2a966b6ce1/href</a></iframe><p>Vamos ter um TypeError na nossa cara.</p><p>Symbols podem ter uma descrição para propósitos de <em>debugging, </em>então podemos fazer algo assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/45c622c55d48239ca4ba9461d87117e2/href">https://medium.com/media/45c622c55d48239ca4ba9461d87117e2/href</a></iframe><p>Ok, entendemos como podemos criar um, mas o que eles são de verdade? Symbols são tipos imutáveis nativos, assim como números ou strings. Mas temos que notar algo diferente, Symbols são <strong><em>únicos</em></strong>, diferentemente da maioria das primitivas que temos por ai.</p><p>Por serem únicos, ao compararmos eles, vamos obter resultados bastante interessantes:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/63a9d90343deb3d664320f754aa83df9/href">https://medium.com/media/63a9d90343deb3d664320f754aa83df9/href</a></iframe><p>Isto acontece porque cada símbolo tem um identificador único que não pode ser alterado, e é este identificador que faz a comparação entre eles. Da mesma forma, se utilizarmos typeof Symbol() ou typeof Symbol(&#39;desc&#39;) vamos sempre obter symbol .</p><p>Symbols são acessíveis de três formas que vamos explorar mais abaixo, mas tenha em mente algumas informações importantes:</p><ul><li>Symbols são acessíveis entre <em>realms, </em>por <em>realm</em> queremos dizer <em>contexto</em>, por exemplo, sua página é um contexto de document enquanto, dentro dela, podemos ter um &lt;iframe&gt; com um contexto/realm diferente.</li><li>Você pode registrar Symbols globais e acessa-los por entre esses contextos também</li><li>Uma das classes de Symbols é chamada de &quot;Well-Known&quot;, eles existem entre <em>realms</em>, mas não são acessíveis no registro global de Symbols</li></ul><h3>&quot;Runtime-wide&quot; — Acessível pelo runtime</h3><p>Existem duas formas que você pode utilizar para adicionar Symbols no escopo do <em>runtime</em> do sistema, ambas são através de métodos existentes dentro das APIs do próprio Symbol, o primeiro éSymbol.for(chave) .</p><p>Basicamente o que este método faz é verificar se existe um Symbol no registro global que seja associado a esta chave, se existir, ele é retornado, se não, ele é criado e retornado. Então vamos estudar um pouco as chamadas:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5841d4298df14f9370c6390fe31ac9b1/href">https://medium.com/media/5841d4298df14f9370c6390fe31ac9b1/href</a></iframe><p>Veja que arranjamos um jeito de buscar os símbolos, mesmo eles sendo únicos. Isto acontece porque o registro global de Symbols mantém uma chave para cada Symbol criado, e isto é o identificador que podemos utilizar para encontrar esses valores neste escopo.</p><blockquote>Note somente que, quando criamos uma &quot;chave&quot; usando Symbol.for(&#39;chave&#39;) , este valor se torna também nossa descrição.</blockquote><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2Fl0MYOiRtXulpkInHW%2Ftwitter%2Fiframe&amp;display_name=Giphy&amp;url=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2Fl0MYOiRtXulpkInHW%2Fgiphy.gif&amp;image=https%3A%2F%2Fi.giphy.com%2Fmedia%2Fl0MYOiRtXulpkInHW%2Fgiphy.gif&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=giphy" width="435" height="244" frameborder="0" scrolling="no"><a href="https://medium.com/media/ecc5ac61bb55c7e364a7a4ade81f71ce/href">https://medium.com/media/ecc5ac61bb55c7e364a7a4ade81f71ce/href</a></iframe><p>Outra coisa <strong>importantíssima</strong> de se notar é que Symbols é o mais global que você vai chegar de qualquer outro tipo de variável global, porque além de eles serem acessíveis no seu código e no seu &quot;realm&quot; eles também estão disponíveis em contextos alheios. Então tome muito cuidado ao criar Symbols usando nomes genéricos como &quot;user&quot;, &quot;sort&quot; e coisas do tipo, porque você pode acabar sobrescrevendo um valor importante do sistema.</p><h4>Symbol.keyFor</h4><p>De forma análoga, podemos encontrar a chave que foi associada a um Symbol através do método Symbol.keyFor(symbol) que, dado um Symbol criado previamente, vai nos retornar a chave associada a ele:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1f6ce737cad4bd9d6201e8a7118b3a9b/href">https://medium.com/media/1f6ce737cad4bd9d6201e8a7118b3a9b/href</a></iframe><p>Dizer &quot;Runtime-wide&quot; significa simplesmente dizer que vamos poder acessar estes Symbols entre contextos, mais ou menos assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/455d1a6fc563db6fde8bcc69b218dbbd/href">https://medium.com/media/455d1a6fc563db6fde8bcc69b218dbbd/href</a></iframe><p>Veja que podemos acessar o mesmo Symbol de dentro de outra window presente no iframe.</p><h3>&quot;Well-known&quot; — Os originais</h3><p><em>Well known</em>, traduzido, significa &quot;Bem conhecido&quot;, mas, na verdade, esses Symbols não tem nada de conhecidos. Eles só são chamados assim porque fazem parte do core da linguagem, são <em>built-ins</em>. Antes do ES6, nenhum desses Symbols eram expostos, mas agora podemos brincar com eles.</p><p>Um grande exemplo foi utilizado em <a href="https://medium.com/trainingcenter/iterators-em-javascript-880adef14495">outro artigo sobre iterators</a> que escrevi. Ele é utilizado para definir o método@@iterator em objetos que são aderentes ao protocolo de iteração. Além deste existe <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Well-known_symbols">uma lista de <em>Well-Known Symbols </em>na MDN</a>, muitos deles são bastante conhecidos pela gente como o replace ou o match .</p><h4><strong>Symbol.match</strong></h4><p>De acordo com a documentação, podemos setar o Symbol.match em expressões regulares para false , de forma que faremos elas se comportarem como strings literais quando estamos comparando uns com os outros (principalmente na hora de usar métodos como startsWith , endsWith e includes ):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/36da55b31892ee44640dd5fb3452a52b/href">https://medium.com/media/36da55b31892ee44640dd5fb3452a52b/href</a></iframe><p>Ou, podemos simplesmente usar o toString() que temos o mesmo resultado:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5c80f491bebdfdce8a66e66e264d58c3/href">https://medium.com/media/5c80f491bebdfdce8a66e66e264d58c3/href</a></iframe><p>Outros usos legais destes Symbols podem ser encontrados <a href="https://www.keithcirkel.co.uk/metaprogramming-in-es6-symbols/">aqui</a>.</p><h4>Acesso global</h4><p>Well known Symbols são únicos, mas <strong>são compartilhados entre contextos</strong>, mesmo que eles não sejam acessíveis através do registro global que comentamos acima, vamos usar o mesmo código:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3fc9c3336d32e9df345bfedbc29d9fa4/href">https://medium.com/media/3fc9c3336d32e9df345bfedbc29d9fa4/href</a></iframe><p>No entanto não podemos fazer isso:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bf5dc614bb9daf9dfb6ddb70f9a8eb03/href">https://medium.com/media/bf5dc614bb9daf9dfb6ddb70f9a8eb03/href</a></iframe><h3>Iterando por Symbols</h3><p>Um uso interessante deste tipo é que eles podem ser usados como chaves de objetos, mais ou menos dessa forma:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a6b2d72ead919c43a84b5a8dd3fb6e0c/href">https://medium.com/media/a6b2d72ead919c43a84b5a8dd3fb6e0c/href</a></iframe><p>Ao executarmos console.log(foo) ou qualquer coisa do tipo, vamos obter a representação em string desse objeto, já que os consoles estão inteligentes o suficiente para buscar essa informação. No entanto, se rodarmos Object.keys(foo) vamos ter somente [&#39;propriedade&#39;] . Isto porque o método keys de Object ignora completamente os Symbols. O mesmo vale para JSON.stringify , que vai simplesmente nos retornar {&quot;propriedade&quot;: &quot;legal&quot;} .</p><p>Podemos ir ainda mais longe tentando usar um for ... in , ou até um Object.getOwnPropertyNames(foo) , mas todos eles vão ignorar os Symbols. Se você quiser realmente buscar esses valores, você precisa <strong>saber o que está procurando</strong>. Existe um método próprio para iterar por Symbols em um objeto, que é o <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols">Object.getOwnPropertySymbols</a> . Se executarmos o trecho abaixo, vamos ter uma lista de todos os Symbols que criamos e poderemos iterar por eles, mais ou menos dessa forma:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/62b4b69c2bb84b5c6892f4bead5c1370/href">https://medium.com/media/62b4b69c2bb84b5c6892f4bead5c1370/href</a></iframe><h3>Casos de uso</h3><p>Até aqui só falamos muito sobre Symbols, mas para que raios você vai usar essa coisa!?</p><p>Temos alguns casos mais comuns que vou explicar por cima:</p><ul><li><strong>Conflito de nomes: </strong>Neste caso podemos tirar proveito da natureza única do Symbol para evitar nomes repetidos em chaves de objetos, assim como fizemos no exemplo anterior.</li><li><strong>Privacidade:</strong> Se estamos criando um framework ou uma lib que precisa de algumas propriedades bem seguras, podemos tirar proveito de que nenhuma propriedade iterável que é largamente conhecida chega a iterar por Symbols, então podemos criar um objeto de configurações (ou metadados), por exemplo, e colocar vários Symbols por lá para definirmos nossas diretrizes de operação. Basta que você itere por ela usando Object.getOwnPropertySymbols .</li><li><strong>Constantes:</strong> Geralmente usamos uma string ou número para definir uma constante, por exemplo, o nível de um log, que pode ser definido como log.levels = { INFO: &#39;info&#39;, DEBUG: &#39;debug&#39; } mas isso não os torna realmente globais seguros, porque podemos criar outros valores iguais e, ao invés de usar nossa função como log(log.levels.DEBUG, &#39;mensagem&#39;) podemos simplesmente fazer log(&#39;debug&#39;, &#39;mensagem&#39;) . Então podemos utilizar Symbols como log.levels = { INFO: Symbol(&#39;info&#39;), DEBUG: Symbol(&#39;debug&#39;) } para prevenir isso</li></ul><p>Em suma, Symbols são uma ótima maneira de atrelar propriedades em objetos de uma forma simples, ou então de criar diretrizes que seu programa deve seguir por baixo dos panos.</p><p>Se eu for ficar falando de todos os casos de uso de Symbols, provavelmente este artigo ficará imenso, então vou me ater a um último caso de uso que merece um pouco de explicação.</p><h4>Definir protocolos e Hooks</h4><p>Este é o maior caso de uso e, com certeza o mais legal de todos. Hoje, muitas libs populares utilizam protocolos sobre os quais alguns objetos devem aderir (como você pode ver <a href="https://ponyfoo.com/articles/es6-symbols-in-depth">aqui</a>), não precisamos ir muito longe, o próprio Symbol.iterator é um protocolo a qual todos os Arrays aderem, e isto define que o objeto pode ser iterado.</p><blockquote>Basicamente, ao executar um for ... of o Javascript faz uma checagem se Object[Symbol.iterator] existe, se sim, então parabéns, você tem um iterador, se não, você não pode iterar por esse objeto.</blockquote><p>Vamos usar um caso mais interessante, se quisermos criar um protocolo que diz de qual forma um objeto deve ser transformado em string para ser printado na tela (basicamente o que o toString() faz). Podemos criar um Symbol.for(&#39;object.upper&#39;) e colocar este Symbol em qualquer um dos nossos objetos:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c03306ee279fce0323ad9a988132a0cd/href">https://medium.com/media/c03306ee279fce0323ad9a988132a0cd/href</a></iframe><p>O exemplo anterior não irá funcionar porque não implementamos a lógica no console e nem no object.keys para olhar o valor de obj[Symbol.for(&#39;object.upper&#39;)] e printar tudo em maiúsculo, mas deu para entender a ideia. Uma implementação disto para o console seria algo assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f9bd16e8b3638c052d55cfdd7da454d8/href">https://medium.com/media/f9bd16e8b3638c052d55cfdd7da454d8/href</a></iframe><p>Nada nos impede de adicionar uma função em um Symbol dentro de um objeto, criando o que é chamado de <strong>hook</strong>, funções que executam em determinadas partes do código respondendo a determinados métodos. Podemos, por exemplo, alterar o comportamento de como o console itera e inspeciona objetos por uma função própria, que é chamada sempre que o objeto é convertido para string.</p><h3>Conclusão</h3><p>Symbols são obscuros e achar uma utilidade para eles é realmente muito complicado, provavelmente ninguém vai ter que se preocupar em utilizar ou mexer em Symbols tão cedo.</p><p>No entanto, o entendimento que o Symbol proporciona para o programador acerca do funcionamento da linguagem é fora do normal. Apenas entendendo os iterators, somos capazes de deduzir como loops como for e for ... of funcionam sem precisar de muito esforço. Tornando nosso código muito mais interessante.</p><p>Não deixe de acompanhar mais do meu conteúdo no <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=medium_post">meu blog</a> e se inscreva na <a href="https://blog.lsantos.dev/signup?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=medium_post">newsletter</a> para receber notícias semanais!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=383e359e64e3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/trainingcenter/javascript-symbols-decifrando-o-mist%C3%A9rio-383e359e64e3">Javascript Symbols: Decifrando o mistério</a> was originally published in <a href="https://medium.com/trainingcenter">Training Center</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>