<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Eddy Nguyen</title>
    <description>The latest articles on Forem by Eddy Nguyen (@eddeee888).</description>
    <link>https://forem.com/eddeee888</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F556967%2F07a671c9-2c17-4da0-9e3f-1e045f0a1bbd.jpg</url>
      <title>Forem: Eddy Nguyen</title>
      <link>https://forem.com/eddeee888</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/eddeee888"/>
    <language>en</language>
    <item>
      <title>Schema-driven development in 2021</title>
      <dc:creator>Eddy Nguyen</dc:creator>
      <pubDate>Thu, 15 Jul 2021 10:25:28 +0000</pubDate>
      <link>https://forem.com/eddeee888/schema-driven-development-in-2021-2708</link>
      <guid>https://forem.com/eddeee888/schema-driven-development-in-2021-2708</guid>
      <description>&lt;p&gt;Schema-driven development is an important concept to know in 2021. A lot of full-stack applications are built with schema-driven technologies. Schema-driven development could help teams build products better and faster. What exactly is schema-driven development? What are the benefits of schema-driven development? We will explore the answers to these questions in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is schema-driven development?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The schema is a contract
&lt;/h3&gt;

&lt;p&gt;First, let's understand what a schema is. A schema is a &lt;em&gt;contract&lt;/em&gt; between two sides of a system. The schema communicates the type of requests that can be made and the expected type of response. &lt;/p&gt;

&lt;p&gt;Depending on the context where the schema is used, each side could play a different role. For example, in traditional web applications, the side making a request is the client or browser. The side returning a response is the application server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7byqt1ej1oc1ppf9v5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7byqt1ej1oc1ppf9v5s.png" alt="Diagram of a Client making request to a Server following schema specifications"&gt;&lt;/a&gt;Example of a client making request to a server based on schema specification&lt;/p&gt;

&lt;p&gt;A schema follows a specific, unambiguous type of language defined by the technology that you use. Schema language is usually programming-language agnostic that communicates common software ideas such as objects, enumeration, field types (e.g. string, integer). Some common schema languages you may have encountered before are YAML and JSON. The client and server should understand these concepts to fulfil their part of the contract. &lt;/p&gt;

&lt;p&gt;If we think a system works like a restaurant where the client is the "customer" and the server is the "wait staff". Then, the schema is the "menu". Given a menu, the customer can quickly scan through to find the "dish" (or resource) to consume. In some cases, the customer could see the ingredients and tell to the wait staff to remove items from the final dish.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xr7vp4x0tn13uxfuu2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xr7vp4x0tn13uxfuu2v.png" alt="SDL-first vs code-first"&gt;&lt;/a&gt;The schema is the menu. Via &lt;a href="https://unsplash.com/photos/Ba93EBcUVMg" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Schema-driven development ( SDD ) definition
&lt;/h3&gt;

&lt;p&gt;SDD prioritises the design of the schema and uses it as the first-class citizen to communicate the responsibilities of the client and the server. This contract usually becomes the API.&lt;/p&gt;

&lt;p&gt;Let’s say we have a web application being built by two teams: Frontend and Backend.&lt;/p&gt;

&lt;p&gt;A non-SDD development process may look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Frontend team contacts Backend team for data that Backend team owns.&lt;/li&gt;
&lt;li&gt;Backend team receives the request and starts to create the endpoint that returns the data Frontend team needs.&lt;/li&gt;
&lt;li&gt;Backend team finishes the endpoint and lets Frontend team know.&lt;/li&gt;
&lt;li&gt;Frontend team tries out the endpoint but it may not have what they need.&lt;/li&gt;
&lt;li&gt;Backend team goes back and fixes the endpoint.&lt;/li&gt;
&lt;li&gt;Repeat steps 3-6 until Frontend team gets what they need.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the other hand, the SSD process may look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Frontend team proposes changes to the schema.&lt;/li&gt;
&lt;li&gt;Backend team understands exactly what they need to implement.&lt;/li&gt;
&lt;li&gt;While Backend team is creating an endpoint that fulfil their part of the contract, Frontend team already knows what that looks like and can mock it out.&lt;/li&gt;
&lt;li&gt;Both teams finish their side of the contract.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Just because you are working with a technology that has a schema does &lt;em&gt;not&lt;/em&gt; mean you are applying SDD. The schema design and discussions must be front and centre for the process to be called SDD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of schema-driven development
&lt;/h2&gt;

&lt;p&gt;There are many benefits of schema-driven development, which allows teams to build better and faster applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Better cross-team communications
&lt;/h3&gt;

&lt;p&gt;Effective communication is unambiguous, concise and intentional. These are also the attributes of a schema language. When a team proposes changes to a schema, the team on the other side should know what must be done.&lt;/p&gt;

&lt;p&gt;In fact, I was in numerous projects where complicated changes can be understood from reading the schema proposals.&lt;/p&gt;

&lt;p&gt;Without writing a single line of code. &lt;br&gt;
Without countless chat messages. &lt;br&gt;
Without tortuous video call meetings. &lt;/p&gt;

&lt;p&gt;We just nodded at each other, and knew exactly what must be done. We understood each other as if we somehow develop &lt;a href="https://en.wikipedia.org/wiki/Professor_X" rel="noopener noreferrer"&gt;Professor X&lt;/a&gt;'s level of telepathic power.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Better API design
&lt;/h3&gt;

&lt;p&gt;SDD forces developers to think about designing the contract in abstract schema language. This frees up any implementation details from the conversation. If two teams are involved, they can see whether they can fulfil their part of the contract and suggest changes early. This usually results in well thought out API design for both sides of the system.&lt;/p&gt;

&lt;p&gt;If a side cannot fulfil their part of the schema after the implementation phase has started, they can let the other side know and propose different schema changes. This saves a lot of time and money because errors caught in the design process or early in the implementation phase are much cheaper to fix.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Independent client and server development
&lt;/h3&gt;

&lt;p&gt;Each side of a schema should know exactly what the other side can provide. This knowledge allows development to happen at the same time because we can mock out each side’s payload. In a scenario of a SDD web application, the frontend team should know an endpoint’s response from the schema and therefore can mock said response to code their part without needing to wait for the backend team to do any work.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Clear entity relationships
&lt;/h3&gt;

&lt;p&gt;In software development, we usually need to identify various entities and how they interact with each other. Schema is a great way to represent said relationships. &lt;/p&gt;

&lt;p&gt;For example, a &lt;code&gt;Farm&lt;/code&gt; entity may house many &lt;code&gt;Bull&lt;/code&gt; entities and one &lt;code&gt;Bull&lt;/code&gt; entity can only stay in one &lt;code&gt;Farm&lt;/code&gt; entity at a time. This relationship can be written in the following made-up &lt;strong&gt;Bull Schema Language&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Bull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Language&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;BS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Language&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Bull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Farm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;bulls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bull&lt;/span&gt;&lt;span class="err"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;An&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bulls&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Type-safety
&lt;/h3&gt;

&lt;p&gt;Type-safety is important when building medium to large applications. A lot of bugs can be caught at compile time by using languages like Go or TypeScript.&lt;/p&gt;

&lt;p&gt;A schema has information about the types and interfaces of entities, requests and responses. There are usually tooling in the ecosystem that could help generate code which conforms to the types declared in the schema.&lt;/p&gt;

&lt;p&gt;When code can be generated from the schema, it helps cut down a lot of time and effort in development as developers can focus on the business logic, rather than writing boilerplate-y code to send requests and responses. Some examples of code generators are: &lt;a href="https://www.graphql-code-generator.com/" rel="noopener noreferrer"&gt;GraphQL Code Generator&lt;/a&gt;, &lt;a href="https://github.com/swagger-api/swagger-codegen" rel="noopener noreferrer"&gt;Swagger Codegen&lt;/a&gt; etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples of schema-driven development technologies
&lt;/h2&gt;

&lt;p&gt;In 2021, SDD can be applied end-to-end when building applications. In this section, let’s look at some examples of SDD technologies.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt; is developed by Facebook initially for their mobile app and has been adopted widely across web apps.&lt;/p&gt;

&lt;p&gt;GraphQL's schema is called &lt;strong&gt;Schema Definition Language (SDL)&lt;/strong&gt;. Given a schema, GraphQL clients can declare the fields they want to query. This drastically reduces the amount of data that is sent over the internet because the client usually only needs a subset of an entity attributes. Learn more about &lt;a href="https://graphql.org/learn/schema/" rel="noopener noreferrer"&gt;GraphQL schema&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are numerous implementations of GraphQL clients and servers. You will be spoiled for choice when you are in this space. You can check out a list of outstanding GraphQL related libraries &lt;a href="https://graphql.org/code/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To build clients and servers, you will need consistent tooling and support. The GraphQL open-source community is truly astonishing in this regard. &lt;a href="https://twitter.com/TheGuildDev" rel="noopener noreferrer"&gt;The Guild&lt;/a&gt; is the main powerhouse coordinating a myriad of GraphQL projects.&lt;/p&gt;

&lt;p&gt;Remember how I was telling you code can be generated from the schema? &lt;a href="https://twitter.com/dotansimha" rel="noopener noreferrer"&gt;@dotansimha&lt;/a&gt; takes home the cake for creating a highly versatile plugin-based &lt;a href="https://www.graphql-code-generator.com/" rel="noopener noreferrer"&gt;GraphQL Code Generator&lt;/a&gt; that works for various clients and servers.&lt;/p&gt;

&lt;p&gt;There is also &lt;a href="https://gqlgen.com/" rel="noopener noreferrer"&gt;gqlgen&lt;/a&gt; which is a Go-based server and a code generator. If you want the performance of Go, in a qualified GraphQL server, with type-safety and heaps of other functionalities, gqlgen is the one you’d want. 🤩&lt;/p&gt;

&lt;p&gt;Here's an example in GraphQL schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;bull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bull&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;farm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Farm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Farm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;bulls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Bull&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Common use case&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;Browser/Mobile to Server/s communication&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Clients&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;
&lt;a href="https://relay.dev/" rel="noopener noreferrer"&gt;Relay&lt;/a&gt;, &lt;a href="https://www.apollographql.com/docs/react/" rel="noopener noreferrer"&gt;Apollo Client&lt;/a&gt;, &lt;a href="https://formidable.com/open-source/urql/docs/" rel="noopener noreferrer"&gt;urql&lt;/a&gt;, &lt;a href="https://graphql.org/code/" rel="noopener noreferrer"&gt;and more&lt;/a&gt;
&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Servers&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;
&lt;a href="https://www.apollographql.com/docs/apollo-server/" rel="noopener noreferrer"&gt;Apollo Server&lt;/a&gt;, &lt;a href="https://gqlgen.com/" rel="noopener noreferrer"&gt;gqlgen&lt;/a&gt;, &lt;a href="https://nexusjs.org/" rel="noopener noreferrer"&gt;Nexus&lt;/a&gt;, &lt;a href="https://graphql.org/code/" rel="noopener noreferrer"&gt;and more&lt;/a&gt;
&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt; 

&lt;h4&gt;
  
  
  A quick note about GraphQL and SDD
&lt;/h4&gt;

&lt;p&gt;If you are coming from the wonderful world of GraphQL, you might hear the terms &lt;strong&gt;schema-first development&lt;/strong&gt;, &lt;strong&gt;Schema Definition Language first ( SDL-first )&lt;/strong&gt; and &lt;strong&gt;code-first&lt;/strong&gt; being thrown around a lot. I will use this section to clarify what each term means.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema-first development&lt;/strong&gt; is the process of building software where schema-design is prioritised. This is the same as &lt;strong&gt;schema-driven development&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SDL-first&lt;/strong&gt; is an implementation approach where code is often generated from the schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code-first&lt;/strong&gt; is an implementation approach where resolvers are created first and the schema is generated from the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both &lt;strong&gt;SDL-first&lt;/strong&gt; and &lt;strong&gt;code-first&lt;/strong&gt; approaches in the context of GraphQL &lt;em&gt;can be&lt;/em&gt; schema-driven development if the schema design is the number one focus. It &lt;em&gt;might&lt;/em&gt; seem like you are doing SDD by using the &lt;strong&gt;SDL-first&lt;/strong&gt; approach because both processes start from making changes to the schema. However, it is not SDD if the team providing the resource ( i.e. the Backend team in the example in "schema-driven development definition" section ) does not want to discuss the schema changes up front with the team consuming the resource ( i.e. the Frontend team ).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxunzxs4xhcgsjovz6s81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxunzxs4xhcgsjovz6s81.png" alt="SDL-first vs code-first"&gt;&lt;/a&gt;SDL-first, code-first and schema-driven development - By &lt;a href="https://twitter.com/nikolasburk" rel="noopener noreferrer"&gt;@nikolasburk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For those who are not familiar with GraphQL, you have just stumbled upon something that I'd like to called &lt;strong&gt;GraphQL: Civil War&lt;/strong&gt;. You can do a &lt;a href="https://www.google.com/search?q=sdl-first+vs+code+first&amp;amp;rlz=1C5CHFA_enAU879AU879&amp;amp;oq=sdl-&amp;amp;aqs=chrome.0.69i59j69i57j69i59l2j0l3j69i60.1511j0j7&amp;amp;sourceid=chrome&amp;amp;ie=UTF-8" rel="noopener noreferrer"&gt;Google search on SDL-first vs code-first&lt;/a&gt; to learn more about the ideology of each approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  gRPC/Twirp
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://grpc.io/" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt; and &lt;a href="https://twitchtv.github.io/twirp/docs/intro.html" rel="noopener noreferrer"&gt;Twirp&lt;/a&gt; are Remote Procedure Call frameworks. gRPC is developed by Google and Twirp is developed by &lt;a href="https://www.twitch.tv/" rel="noopener noreferrer"&gt;Twitch&lt;/a&gt;. They both use &lt;a href="https://developers.google.com/protocol-buffers" rel="noopener noreferrer"&gt;Protocol Buffers&lt;/a&gt; (Protobuf) as the &lt;strong&gt;Interface Definition Language&lt;/strong&gt;. Protobuf is also the serialisation protocol for structured data.&lt;/p&gt;

&lt;p&gt;gRPC and Twirp are commonly used in a &lt;a href="https://en.wikipedia.org/wiki/Microservices" rel="noopener noreferrer"&gt;micro-service architecture&lt;/a&gt;. The client is the service making the request and the server is the service returning the response. Services can choose to send data encoded using Protobuf or JSON. Protobuf can be serialised and sent faster than JSON. JSON is useful if you need to debug calls between services.&lt;/p&gt;

&lt;p&gt;They both support code generation for clients and servers in many languages: Go, PHP, Ruby, TypeScript, etc. You can see &lt;a href="https://grpc.io/docs/languages/" rel="noopener noreferrer"&gt;gRPC supported languages here&lt;/a&gt; and &lt;a href="https://github.com/twitchtv/twirp#implementations-in-other-languages" rel="noopener noreferrer"&gt;Twirp supported languages here&lt;/a&gt;. This works out great as companies with micro-service architecture may choose different languages for each service.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://99designs.com" rel="noopener noreferrer"&gt;99designs&lt;/a&gt;, we have backends in different languages: Go, PHP and Ruby. By using Twirp, we allow teams to code in their preferred language while maintaining a consistent, type-safe and convenient way to send data from one service to another.&lt;/p&gt;

&lt;p&gt;Here's an example in Protobuf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;FarmingApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetBull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetBullRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bull&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetFarm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetFarmRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Farm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Bull&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GetBullRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Farm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;repeated&lt;/span&gt; &lt;span class="n"&gt;Bull&lt;/span&gt; &lt;span class="na"&gt;bulls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;GetFarmRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Common use case&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;Server to server communication&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Clients&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;Automatically generated for different languages&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Servers&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;Automatically generated for different languages&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Prisma
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt; is an Object–relational mapping ( ORM ) library which represents database models through a central schema written in &lt;strong&gt;Prisma Schema Language ( PSL )&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Prisma schema is effortlessly easy to read. It clearly shows model relationships and field types. On top of that, other important information such as database connection is also stored in the schema. Learn more about &lt;a href="https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference" rel="noopener noreferrer"&gt;Prisma schema&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From the schema, a &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-client" rel="noopener noreferrer"&gt;TypeScript Prisma Client&lt;/a&gt; can be generated that can be used in Node.js applications - including Next.js! A &lt;a href="https://github.com/prisma/prisma-client-go" rel="noopener noreferrer"&gt;Go Prisma Client&lt;/a&gt; is also in the works.&lt;/p&gt;

&lt;p&gt;On the other side of the schema, Prisma has a sophisticated &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-Migrate" rel="noopener noreferrer"&gt;Prisma Migrate&lt;/a&gt; feature to keep the database in sync with the schema. What's great about this is that it works for multiple types of database: PostgreSQL, MySQL and SQLite with support coming for other types!&lt;/p&gt;

&lt;p&gt;Personally, I normally don't care for traditional ORMs. I think they are a type of abstraction that requires a lot of work to maintain while hardly making the relationships of entities clearer to developers. I'm a &lt;a href="https://eddy.works/blog/i-have-worked-with-lazy-impatient-and-opinionated-software-engineers#the-lazy-engineer" rel="noopener noreferrer"&gt;lazy developer&lt;/a&gt; so anything that's remotely hard is a turn off. &lt;/p&gt;

&lt;p&gt;Prisma is the only ORM that hits the sweet spot for me. It is easy to see the relationships of entities without connecting to the database and click through 200 tables to build up a mental map to connect all the pieces together. The TypeScript Prisma Client is generated with strong type-safety. It's almost like I have two other senior developers watching over me. And when I'm about to do something silly they would be like: "Tsk tsk don't do that, do this!". 😍&lt;/p&gt;

&lt;p&gt;Here's an example in Prisma schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="err"&gt;datasource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;PRISMA_DATABASE_URL&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;generator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;prisma&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Bull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;farm&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Farm&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;farmId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Farm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;bulls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bull&lt;/span&gt;&lt;span class="err"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Common use case&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;Server to database communication&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Clients&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;TypeScript Prisma Client, Go Prisma Client&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;b&gt;Supported databases&lt;/b&gt;&lt;/td&gt;
    &lt;td&gt;PostgreSQL, MySQL and SQLite ( as of July 2021 )&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;Schema-driven development is becoming more relevant in 2021 with many benefits: better cross-team communications, better API design, independent client and server development, clear entity relationships and type-safety. You can easily build a full-stack application with SDD technologies such as GraphQL, Twirp and Prisma. &lt;/p&gt;

&lt;p&gt;I hope you had fun reading this article as much as I did researching and writing it. For similar posts about software engineering, follow me on Twitter &lt;a href="https://twitter.com/eddeee888" rel="noopener noreferrer"&gt;@eddeee888&lt;/a&gt;. 🤙&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>graphql</category>
      <category>protobuf</category>
      <category>prisma</category>
    </item>
    <item>
      <title>I have worked with lazy, impatient &amp; opinionated Software Engineers - here’s what happened</title>
      <dc:creator>Eddy Nguyen</dc:creator>
      <pubDate>Wed, 21 Apr 2021 11:22:40 +0000</pubDate>
      <link>https://forem.com/eddeee888/i-have-worked-with-lazy-impatient-opinionated-software-engineers-here-s-what-happened-4af0</link>
      <guid>https://forem.com/eddeee888/i-have-worked-with-lazy-impatient-opinionated-software-engineers-here-s-what-happened-4af0</guid>
      <description>&lt;p&gt;I have been working in the software industry for almost a decade now. As a result, I was in different teams, some large, some small, and sometimes all by myself. This post will be about my encounters with different types of engineers in the past.&lt;/p&gt;

&lt;h2&gt;
  
  
  😴 The "lazy" engineer
&lt;/h2&gt;

&lt;p&gt;In one of my jobs, I was working with a particularly opinionated &lt;a href="https://en.wikipedia.org/wiki/Web_framework"&gt;web framework&lt;/a&gt;. This was great for my team at the time because it enforced a consistent coding standard. However, this framework also required a lot of &lt;a href="https://en.wikipedia.org/wiki/Boilerplate_code"&gt;boilerplate code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On one rainy morning, after getting assigned a problem to solve, I immediately created the boilerplate code by moving my fingers across the keyboards with the speed and agility comparable to an Olympic 100-metre runner. Then, I noticed one of the engineers barely touched the keyboard at all.&lt;/p&gt;

&lt;p&gt;"How lazy!" - I thought to myself - "He won't have anything to show at due date!"&lt;/p&gt;

&lt;p&gt;When the due date came, we presented our work and, lo and behold, his work is completed with the boilerplate code, business logic, the lot.&lt;/p&gt;

&lt;p&gt;"Impossible! Did his fingers move so fast my eyes could not see them?" - I mumbled as I desperately tried to make sense of the situation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/BA95wfxoSe0KI/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/BA95wfxoSe0KI/giphy.gif" alt="So fast"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I later met the "lazy" engineer at the water cooler to ask about his secret technique. He happily showed me the script he wrote to generate all the boilerplate files that I was furiously typing up. My job became much easier from that day onwards.&lt;/p&gt;

&lt;p&gt;I love to work with "lazy" engineers because they want to &lt;strong&gt;work smart&lt;/strong&gt; and finds ways to do their job more &lt;strong&gt;efficiently&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  😤 The "impatient" engineer
&lt;/h2&gt;

&lt;p&gt;On one of the earliest projects I worked on, we used to use FTP to connect to the server and copy files over to deploy ( I know! I'm a dinosaur! ). This was a manual and tedious process. So when I set up the &lt;a href="https://en.wikipedia.org/wiki/CI/CD"&gt;CI/CD&lt;/a&gt; pipeline for said project, I was happy with myself. I was on cloud nine!&lt;/p&gt;

&lt;p&gt;"I automated the process, I learned from the 'lazy' engineer!" - young me virtually patting myself on the back.&lt;/p&gt;

&lt;p&gt;The process stayed like that for a couple of months. Then, an "impatient" engineer joined the team. &lt;/p&gt;

&lt;p&gt;He complained. A lot. &lt;br&gt;
He complained about how slow the build process was.&lt;br&gt;
He complained about how long it took to completely deliver a new build.&lt;/p&gt;

&lt;p&gt;The young me was very hurt. How dare he attacked the process that I poured my heart and soul into? How could he be so impatient? Building only took 15 minutes and delivery only took about 45 minutes!&lt;/p&gt;

&lt;p&gt;Speed is relative. My "impatient" colleague patiently explained that we could improve the process further. Within an afternoon, he worked his magic and reduced the build time to 3 minutes and delivery time to 10 minutes.&lt;/p&gt;

&lt;p&gt;As I took off my rose coloured glasses, I realised I had been foolishly patient with the existing process and stopped looking for ways to improve it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Kd4eyx8RzYJou6ULey/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Kd4eyx8RzYJou6ULey/giphy.gif" alt="Take off glasses"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I love to work with "impatient" engineers because they want to make things &lt;strong&gt;better&lt;/strong&gt; and &lt;strong&gt;faster&lt;/strong&gt;. They also do not mind getting their hands dirty and get the work done sooner than later.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤨 The "opinionated" engineer
&lt;/h2&gt;

&lt;p&gt;I am the type of person that hates arguments. So I spent a good portion of time earlier in my career not voicing my opinions in meetings, just so I don't have to argue with others.&lt;/p&gt;

&lt;p&gt;One day, an "opinionated" engineer gave me feedback saying he agreed with a lot of my opinions and would love to hear them verbally.&lt;/p&gt;

&lt;p&gt;"Verbally?" - I asked - "Do I normally communicate my opinions non-verbally?"&lt;/p&gt;

&lt;p&gt;"Yes, in your code."&lt;/p&gt;

&lt;p&gt;Mind blown. Coding is a way for software engineers to express themselves. Code does not lie, and it's loud and clear what our intentions and opinions are. Through code, we communicate how we think problems should be solved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l0NwHXQy3kUSfFF60/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l0NwHXQy3kUSfFF60/giphy.gif" alt="Mind blown"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "opinionated" engineer also explained to me that certain ideas are better communicated verbally. Having an opinion is good, voicing it is better and discussing it with others is a great way to materialise ideas into practices, standards and products.&lt;/p&gt;

&lt;p&gt;I love to work with "opinionated" engineers because they &lt;strong&gt;effectively communicate their opinions&lt;/strong&gt; and &lt;strong&gt;help others have their voice heard&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧘 The respectful engineer
&lt;/h2&gt;

&lt;p&gt;One common trait of the engineers I worked with is that they are respectful. A respectful engineer treats all their colleagues with respect, regardless of their job title, background, sex, age, etc.&lt;/p&gt;

&lt;p&gt;An "opinionated" engineer without respect for others' opinions does not listen to others and only wants to do things their way.&lt;/p&gt;

&lt;p&gt;An "impatient" engineer without respect for others' processes probably looks down on them without offering ways to make things better.&lt;/p&gt;

&lt;p&gt;A "lazy" engineer without respect for others' time probably does the minimum just to not get fired.&lt;/p&gt;

&lt;p&gt;A respectful engineer is who I always strive to be and whom I want to work with.&lt;/p&gt;

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

&lt;p&gt;I have worked with "lazy", "impatient" and "opinionated" Software Engineers, and to be honest - I love working with them! Do you have your own favourites? Let me know via Twitter &lt;a href="https://twitter.com/eddeee888"&gt;@eddeee888&lt;/a&gt;. 🤙&lt;/p&gt;

&lt;p&gt;Want to be surrounded by amazing people you'd love to work with? Join the &lt;a href="https://www.duelofdoves.com/"&gt;Duel of Doves&lt;/a&gt; community!&lt;/p&gt;

</description>
      <category>career</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to deploy Prisma in AWS Lambda with Serverless</title>
      <dc:creator>Eddy Nguyen</dc:creator>
      <pubDate>Wed, 24 Feb 2021 20:46:55 +0000</pubDate>
      <link>https://forem.com/eddeee888/how-to-deploy-prisma-in-aws-lambda-with-serverless-1m76</link>
      <guid>https://forem.com/eddeee888/how-to-deploy-prisma-in-aws-lambda-with-serverless-1m76</guid>
      <description>&lt;p&gt;I remember a time when I had to remotely connect to a server using FTP to deliver a web application source code. Those were the "Wild West" days of my career where almost all of the code I wrote were untyped and the manual deployment process could bring down the production website with one wrong keystroke. 😱&lt;/p&gt;

&lt;p&gt;Fast forward to the present, I have complete confidence in the code I write in Node.js and TypeScript, strongly typed domain objects modelled with Prisma, and one keystroke is all I need to deploy a feature to AWS Lambdas, all without a dedicated server. What a time to be alive!&lt;/p&gt;

&lt;p&gt;In this post, I will talk about one of my favorite setups to go from almost zero to deploying Prisma integrated AWS Lambdas with shared Lambda layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;How it works&lt;/li&gt;
&lt;li&gt;Setting up the project&lt;/li&gt;
&lt;li&gt;Setting up Prisma&lt;/li&gt;
&lt;li&gt;Creating a Lambda&lt;/li&gt;
&lt;li&gt;Creating Lambda layers&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;General notes&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🗝️ Prerequisites
&lt;/h2&gt;

&lt;p&gt;To make full use of this guide, make sure you have the following items set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/account/" rel="noopener noreferrer"&gt;AWS account&lt;/a&gt; - free-tier&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;Serverless account&lt;/a&gt; - free-tier&lt;/li&gt;
&lt;li&gt;One &lt;a href="https://aws.amazon.com/rds/" rel="noopener noreferrer"&gt;AWS RDS database&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;One &lt;a href="https://aws.amazon.com/iam/" rel="noopener noreferrer"&gt;AWS IAM user&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛠️ How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Lambda and Lambda layers
&lt;/h3&gt;

&lt;p&gt;An &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; is a computing service that allows you to run code without a server. A Lambda is normally small and has a size limit of 50MB. It may seem like a lot but if we build a Lambda with all the dependencies in &lt;code&gt;node_modules&lt;/code&gt;, it would easily go over the limit.&lt;/p&gt;

&lt;p&gt;A good practice is to keep only the main business logic in the Lambda function. Keeping a Lambda small also means it takes less time to deploy. &lt;/p&gt;

&lt;p&gt;All imports should be treated as &lt;strong&gt;externals&lt;/strong&gt; i.e. as if they come from &lt;code&gt;node_modules&lt;/code&gt;. These externals usually come from Lambda layers. I like to split my layers into 3 ( you can use up to 5 layers ):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Normal runtime dependencies layer&lt;/strong&gt;: This layer contains the runtime dependencies installed from package registries. These are typically declared in package.json's &lt;code&gt;dependencies&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prisma Client layer&lt;/strong&gt;: I like to keep Prisma-related dependencies as their own layer because the way they are generated is fairly different from others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Libs layer&lt;/strong&gt;: This layer includes custom utilities that can be shared between Lambdas and other apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will explore how to create these layers for Node.js Lambdas later in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prisma binary
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt; is an ORM where a TypeScript client is generated based on a schema so consuming apps have type-safety when querying databases. It also creates binaries to run in different environments. We will need to generate different binaries to run in the dev environment and in a Lambda.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless Framework
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;Serverless Framework&lt;/a&gt; is a service that does a lot of heavy lifting when it comes to deploying to AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏡 Setting up the project
&lt;/h2&gt;

&lt;p&gt;Let's start with a minimal setup. It should have the following structure:&lt;/p&gt;

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

prisma/
scripts/
src/
  -- lambdas/
  -- libs/
package.json


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

&lt;/div&gt;

&lt;p&gt;Now, install the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; prisma @prisma/client @types/node ts-node typescript


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

&lt;/div&gt;

&lt;p&gt;Create a &lt;code&gt;tsconfig.json&lt;/code&gt; that looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"es2016"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext.asynciterable"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@libs/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"libs/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"forceConsistentCasingInFileNames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noUnusedLocals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitAny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./src"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;The important setting here is &lt;code&gt;@libs/*: ["libs/*"]&lt;/code&gt;. This is used so we can use &lt;code&gt;@libs&lt;/code&gt; as the alias to import modules from &lt;code&gt;src/libs&lt;/code&gt; locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  🥞 Setting up Prisma
&lt;/h2&gt;

&lt;p&gt;Let's create a minimal Prisma schema:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// ./prisma/schema.prisma&lt;/span&gt;

&lt;span class="n"&gt;datasource&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mysql"&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PRISMA_DATABASE_URL"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;provider&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prisma-client-js"&lt;/span&gt;
  &lt;span class="n"&gt;binaryTargets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PRISMA_BINARY_TARGET"&lt;/span&gt;&lt;span class="o"&gt;)]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;   &lt;span class="n"&gt;Int&lt;/span&gt;    &lt;span class="nd"&gt;@id&lt;/span&gt; &lt;span class="nd"&gt;@default&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autoincrement&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
  &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="nd"&gt;@unique&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There will be a &lt;code&gt;User&lt;/code&gt; table when migrations are run. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env("PRISMA_DATABASE_URL")&lt;/code&gt; is used to provide different Prisma URLs depending on the environment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env("PRISMA_BINARY_TARGET")&lt;/code&gt; is used to generate and run different binaries depending on where we run the code. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The default value of &lt;code&gt;binaryTargets&lt;/code&gt; is &lt;code&gt;native&lt;/code&gt;. This means a Prisma Client will be generated based on the current operating system where the command is run. We will use this in dev. You can learn about &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-schema/generators#the-native-binary-target" rel="noopener noreferrer"&gt;the native binary target here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We want to set this value to &lt;code&gt;rhel-openssl-1.0.x&lt;/code&gt; when we generate the Prisma Client to be used by the Lambda. You can find all of the available &lt;a href="https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#binarytargets-options" rel="noopener noreferrer"&gt;binaryTargets options here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Docker is my first choice when it comes to separating environments but for this example, we will use a &lt;code&gt;.env&lt;/code&gt; file for simplicity:&lt;/p&gt;

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

// ./.env

# Database URL could be different depending on your setup:
PRISMA_DATABASE_URL=mysql://root:root@localhost/lambda_test?schema=public

# `native` is used in dev so it will generate 
# the binary based on your current OS. 
# `rhel-openssl-1.0.x` should be used for AWS lambda.
PRISMA_BINARY_TARGET=native


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

&lt;/div&gt;

&lt;p&gt;Running the following command should create a new database locally:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;yarn prisma migrate dev


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

&lt;/div&gt;

&lt;p&gt;The rest of this guide assumes you have run the same migration on the prod RDS database. For more information, please refer to the &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-migrate" rel="noopener noreferrer"&gt;Prisma migrate documentation&lt;/a&gt;. For this guide's purpose, you can export your dev database structure and content and import them into your RDS database.&lt;/p&gt;

&lt;p&gt;Now, we can create a new lib to initialise a Prisma Client:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// ./src/libs/createPrismaClient.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createPrismaClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We could directly do this in the Lambda but extracting this into a lib has many advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent logic when creating Prisma Clients e.g. we might want to add a middleware to all Clients.&lt;/li&gt;
&lt;li&gt;Can be used by different services e.g. we can use this function in a Node.js web app, Lambdas, or tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👷 Creating a Lambda
&lt;/h2&gt;

&lt;p&gt;Let's create a Lambda that when invoked will create a new user with a UUID into the database. Note that Prisma is able to create UUIDs for records natively but this will demonstrate how the runtime dependencies layer works.&lt;/p&gt;

&lt;p&gt;First, let's install the packages to generate UUID:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add uuid
&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; @types/uuid


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

&lt;/div&gt;

&lt;p&gt;And here's the code for the Lambda:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// ./src/lambdas/insertUser/handler.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@libs/createPrismaClient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Note that we should always run &lt;code&gt;prisma.$disconnect();&lt;/code&gt; at the end of every Lambda to ensure we do not hold connections to the database.&lt;/p&gt;

&lt;p&gt;We will use TypeScript to compile this code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;tsc&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This will be compiled into the &lt;code&gt;build&lt;/code&gt; folder:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// ./build/lambdas/insertUser/handler.js&lt;/span&gt;

&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;__esModule&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prismaClient_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@libs/createPrismaClient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uuid_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prismaClient_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createPrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uuid_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//# sourceMappingURL=handler.js.map&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This file can be uploaded to AWS as a Lambda function. However, it's importing &lt;code&gt;@libs/createPrismaClient&lt;/code&gt; and &lt;code&gt;uuid&lt;/code&gt; from &lt;code&gt;node_modules&lt;/code&gt;. We must create the Lambda layers that hold these dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧅 Creating Lambda layers
&lt;/h2&gt;

&lt;p&gt;As mentioned above, there are 3 Lambda layers we want to create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime dependencies layer&lt;/li&gt;
&lt;li&gt;Prisma layer&lt;/li&gt;
&lt;li&gt;Libs layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Lambda layer is a zip file that looks like this:&lt;/p&gt;

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

layer.zip
  -- nodejs
     -- node_modules
        -- lib1
        -- lib2
        ...


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

&lt;/div&gt;

&lt;p&gt;You can read more about &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html" rel="noopener noreferrer"&gt;Lambda layers here&lt;/a&gt;. We will need to create one such file for each layer:&lt;/p&gt;

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

// Runtime dependencies layer

lambda-layers-node_modules.zip
  -- nodejs
     -- node_modules
        -- uuid


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

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

// Prisma layer

lambda-layers-prisma-client.zip
  -- nodejs
     -- node_modules
        -- .prisma
        -- @prisma


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

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

// Libs layer

lambda-layers-libs.zip
  -- nodejs
     -- node_modules
        -- @libs
           -- createPrismaClient.js


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

&lt;/div&gt;

&lt;p&gt;Let's create 3 separate scripts that can prepare these layers for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Runtime dependencies layer
&lt;/h3&gt;

&lt;p&gt;This script should be in &lt;code&gt;./scripts/ci/prepare-node-modules-lambda-layer.sh&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;prepare_node_modules_lambda_layer&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning up workspace ..."&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; lambda-layers-node_modules

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating layer ..."&lt;/span&gt;
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; lambda-layers-node_modules/nodejs

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Prepare server node_modules lambda layer ..."&lt;/span&gt;
  &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; node_modules lambda-layers-node_modules/nodejs

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Compressing ..."&lt;/span&gt;
  &lt;span class="nb"&gt;pushd &lt;/span&gt;lambda-layers-node_modules &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-zcf&lt;/span&gt; /tmp/nodejs.tar.gz &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv&lt;/span&gt; /tmp/nodejs.tar.gz ./nodejs.tar.gz

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Remove unzipped files ..."&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; nodejs

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stats:"&lt;/span&gt;
  &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; nodejs.tar.gz

  &lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
prepare_node_modules_lambda_layer


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

&lt;/div&gt;

&lt;p&gt;Note: this script creates a zip file that contains everything in &lt;code&gt;node_modules&lt;/code&gt; so if you generated a Prisma Client here, it will be included in this layer. This script is intended to be used in CI/CD in a step where we only install production package.json dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prisma layer
&lt;/h3&gt;

&lt;p&gt;This script should be in &lt;code&gt;./scripts/ci/prepare-prisma-client-lambda-layer.sh&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;prepare_prisma_client_lambda_layer&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning up workspace ..."&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; lambda-layers-prisma-client

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating layer ..."&lt;/span&gt;
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; lambda-layers-prisma-client/nodejs/node_modules/.prisma
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; lambda-layers-prisma-client/nodejs/node_modules/@prisma

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Prepare Prisma Client lambda layer ..."&lt;/span&gt;
  &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; node_modules/.prisma/client lambda-layers-prisma-client/nodejs/node_modules/.prisma
  &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; node_modules/@prisma lambda-layers-prisma-client/nodejs/node_modules

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Remove Prisma CLI..."&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; lambda-layers-prisma-client/nodejs/node_modules/@prisma/cli

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Compressing ..."&lt;/span&gt;
  &lt;span class="nb"&gt;pushd &lt;/span&gt;lambda-layers-prisma-client &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-zcf&lt;/span&gt; /tmp/nodejs.tar.gz &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv&lt;/span&gt; /tmp/nodejs.tar.gz ./nodejs.tar.gz

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Remove unzipped files ..."&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; nodejs

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stats:"&lt;/span&gt;
  &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; nodejs.tar.gz

  &lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
prepare_prisma_client_lambda_layer


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

&lt;/div&gt;

&lt;p&gt;When this script is run, it will create a Lambda layer with the &lt;code&gt;.prisma&lt;/code&gt; and &lt;code&gt;@prisma&lt;/code&gt; directories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@prisma&lt;/code&gt; is where the generators and wiring happen. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.prisma&lt;/code&gt; contains the generated TypeScript interfaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also removes &lt;code&gt;@prisma/cli&lt;/code&gt; to keep the layer smaller since we won't be running commands in the Lambda. In later versions of Prisma ( &amp;gt;=2.16 ), this package is no longer needed so you can omit this line.&lt;/p&gt;

&lt;p&gt;This should be run in CI/CD after the Prisma packages have been installed and the Prisma Client generated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Libs layer
&lt;/h3&gt;

&lt;p&gt;This script should be in &lt;code&gt;./scripts/ci/prepare-libs-lambda-layer.sh&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;prepare_libs_lambda_layer&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning up ..."&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; lambda-layers-libs

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating layer ..."&lt;/span&gt;
  &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; lambda-layers-libs/nodejs/node_modules/@libs
  &lt;span class="nb"&gt;mv &lt;/span&gt;build/libs build/@libs

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Prepare libs lambda layer ..."&lt;/span&gt;
  &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; build/@libs lambda-layers-libs/nodejs/node_modules

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Compressing ..."&lt;/span&gt;
  &lt;span class="nb"&gt;pushd &lt;/span&gt;lambda-layers-libs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-zcf&lt;/span&gt; /tmp/nodejs.tar.gz &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv&lt;/span&gt; /tmp/nodejs.tar.gz ./nodejs.tar.gz

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Remove unzipped files ..."&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; nodejs

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stats:"&lt;/span&gt;
  &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; nodejs.tar.gz

  &lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
prepare_libs_lambda_layer


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

&lt;/div&gt;

&lt;p&gt;This script should be run after we have compiled the libs using &lt;code&gt;yarn tsc&lt;/code&gt;. Note that this layer will be built as &lt;code&gt;build/libs&lt;/code&gt; but we rename it into &lt;code&gt;node_modules/@libs&lt;/code&gt; to match the module import path that we have in the Lambda.&lt;/p&gt;

&lt;h2&gt;
  
  
  ☁️ Deployment
&lt;/h2&gt;

&lt;p&gt;At this point, we could manually upload the Lambda and its layers' zips to AWS but it would take forever to do it this way. I got you fam, don't worry. 😉&lt;/p&gt;

&lt;p&gt;We'll set up some CI/CD goodness in this section. &lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless
&lt;/h3&gt;

&lt;p&gt;This is where Serverless Framework comes in. We can deploy everything with one command. Create a serverless yml like this: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# ./serverless.yml&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prisma-aws-lambda-deployment&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs12.x&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:AWS_REGION}&lt;/span&gt;
  &lt;span class="c1"&gt;# vpc:&lt;/span&gt;
  &lt;span class="c1"&gt;#   securityGroupIds:&lt;/span&gt;
  &lt;span class="c1"&gt;#     -  # FILLME&lt;/span&gt;
  &lt;span class="c1"&gt;#   subnetIds:&lt;/span&gt;
  &lt;span class="c1"&gt;#     -  # FILLME&lt;/span&gt;
  &lt;span class="c1"&gt;#     -  # FILLME&lt;/span&gt;

&lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;TopicPrismaAwsNodeModules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-node_modules&lt;/span&gt;
  &lt;span class="na"&gt;TopicPrismaAwsLibs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-libs&lt;/span&gt;
  &lt;span class="na"&gt;TopicPrismaAwsPrismaClient&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-prisma-client&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;insertUserCron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;insertUser/handler.default&lt;/span&gt;
    &lt;span class="na"&gt;memorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;290&lt;/span&gt; &lt;span class="c1"&gt;# 4 minutes 50 seconds&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rate(5 minutes)&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
      &lt;span class="na"&gt;PRISMA_DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:PRISMA_DATABASE_URL}&lt;/span&gt;
      &lt;span class="na"&gt;PRISMA_BINARY_TARGET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:PRISMA_BINARY_TARGET}&lt;/span&gt;
    &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;TopicPrismaAwsNodeModulesLambdaLayer&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;TopicPrismaAwsLibsLambdaLayer&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;TopicPrismaAwsPrismaClientLambdaLayer&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In this file, we basically deploy the &lt;code&gt;insertUser&lt;/code&gt; Lambda with all the layers attached as a cron and run it every 5 mins. Remember to set your VPC settings so your Lambda can send requests to the RDS instance!&lt;/p&gt;

&lt;p&gt;Other ways to invoke a Lambda function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a request to the Lambda URL&lt;/li&gt;
&lt;li&gt;Execute the &lt;a href="https://docs.aws.amazon.com/it_it/cli/latest/reference/lambda/invoke.html" rel="noopener noreferrer"&gt;AWS CLI invoke command&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Use the test feature in the AWS Lambda UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, this file is intended to be run in the &lt;code&gt;build/&lt;/code&gt; directory. You will see an error if you try to run this from the project root. Don't worry, it'll make sense in CI/CD.&lt;/p&gt;

&lt;h3&gt;
  
  
  Github action
&lt;/h3&gt;

&lt;p&gt;In the previous sections, I have been alluding to deploying in CI/CD. We will be using GitHub action but you can do this in any other providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build runtime dependencies layer&lt;/li&gt;
&lt;li&gt;Build Prisma layer&lt;/li&gt;
&lt;li&gt;Build libs layer&lt;/li&gt;
&lt;li&gt;Build Lambda(s)&lt;/li&gt;
&lt;li&gt;Once all the previous steps are done, download the artifacts and deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the GitHub action file for it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# ./.github/workflows/deploy-lambdas.yml&lt;/span&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy lambdas&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;master"&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-node_modules-lambda-layer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bld. node_modules layer&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js 12.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&lt;/span&gt;

      &lt;span class="c1"&gt;# Only install PROD packages i.e. no `@types/*` packages or dev-related packages&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install PROD packages&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn --production&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare lambda layer&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./scripts/ci/prepare-node-modules-lambda-layer.sh&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-node_modules&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./lambda-layers-node_modules&lt;/span&gt;

  &lt;span class="na"&gt;build-prisma-client-lambda-layer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bld. @prisma/client layer&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js 12.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install ALL packages&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn --frozen-lockfile&lt;/span&gt;

      &lt;span class="c1"&gt;# Generate Prisma Client and binary that can run in a lambda environment&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare prisma client&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn PRISMA_BINARY_TARGET=rhel-openssl-1.0.x prisma generate&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare "@prisma/client" lambda layer&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./scripts/ci/prepare-prisma-client-lambda-layer.sh&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-prisma-client&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./lambda-layers-prisma-client&lt;/span&gt;

  &lt;span class="na"&gt;build-libs-lambda-layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bld. @libs layer&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js 12.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install ALL packages&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn --frozen-lockfile&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare prisma client&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn PRISMA_BINARY_TARGET=rhel-openssl-1.0.x prisma generate&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build assets&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn tsc&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare "@libs/*"" lambda layer&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./scripts/ci/prepare-libs-lambda-layer.sh&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-libs&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./lambda-layers-libs&lt;/span&gt;

  &lt;span class="na"&gt;build-lambdas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build lambdas&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js 12.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install ALL packages&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn --frozen-lockfile&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare prisma client&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn PRISMA_BINARY_TARGET=rhel-openssl-1.0.x prisma generate&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build lambdas&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn tsc&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-lambdas&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build/lambdas&lt;/span&gt;

  &lt;span class="na"&gt;deploy-lambdas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy lambdas&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="nv"&gt;build-node_modules-lambda-layer&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;build-prisma-client-lambda-layer&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;build-libs-lambda-layers&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;build-lambdas&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js 12.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-lambdas&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build/lambdas&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-node_modules&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build/lambdas/lambda-layers-node_modules&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-libs&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build/lambdas/lambda-layers-libs&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda-layers-prisma-client&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build/lambdas/lambda-layers-prisma-client&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Unzip layers&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;tar -C ./build/lambdas/lambda-layers-node_modules -xf ./build/lambdas/lambda-layers-node_modules/nodejs.tar.gz&lt;/span&gt;
          &lt;span class="s"&gt;rm -rf ./build/lambdas/lambda-layers-node_modules/nodejs.tar.gz&lt;/span&gt;
          &lt;span class="s"&gt;tar -C ./build/lambdas/lambda-layers-libs -xf ./build/lambdas/lambda-layers-libs/nodejs.tar.gz&lt;/span&gt;
          &lt;span class="s"&gt;rm -rf ./build/lambdas/lambda-layers-libs/nodejs.tar.gz&lt;/span&gt;
          &lt;span class="s"&gt;tar -C ./build/lambdas/lambda-layers-prisma-client -xf ./build/lambdas/lambda-layers-prisma-client/nodejs.tar.gz&lt;/span&gt;
          &lt;span class="s"&gt;rm -rf ./build/lambdas/lambda-layers-prisma-client/nodejs.tar.gz&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Move serverless.yml&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mv serverless.yml ./build/lambdas/serverless.yml&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy lambdas and layers&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aaronpanch/action-serverless@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy --debug&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SERVICE_ROOT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build/lambdas&lt;/span&gt;
          &lt;span class="na"&gt;SERVERLESS_ACCESS_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVERLESS_ACCESS_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID_CI }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY_CI }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_REGION }}&lt;/span&gt;
          &lt;span class="na"&gt;PRISMA_DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PRISMA_DATABASE_URL }}&lt;/span&gt;
          &lt;span class="na"&gt;PRISMA_BINARY_TARGET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rhel-openssl-1.0.x&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rhel-openssl-1.0.x&lt;/code&gt; is the AWS Lambda binary target for Prisma Client. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;serverless.yml&lt;/code&gt; is moved into &lt;code&gt;./build/lambdas/serverless.yml&lt;/code&gt; to make sure it only compresses the built Lambda folder.&lt;/li&gt;
&lt;li&gt;Serverless zips layers automatically so we need to unzip the layers before running the &lt;code&gt;serverless deploy&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secrets.*&lt;/code&gt; values can be set using &lt;a href="https://docs.github.com/en/actions/reference/encrypted-secrets" rel="noopener noreferrer"&gt;GitHub action secrets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SERVERLESS_ACCESS_KEY&lt;/code&gt; can be created through the Serverless dashboard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; can be created when you create an IAM user in AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will see something like this in the "Actions" tab of your Github repo if everything went according to plan:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9lilrdk5s7q8bgmjohv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9lilrdk5s7q8bgmjohv.png" alt="Screen Shot 2021-02-21 at 9.19.44 am"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And... that's it! If the gods of AWS are on your side, you'll start seeing new users being inserted into your &lt;code&gt;User&lt;/code&gt; table with Prisma! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 General notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Make sure to secure your VPC, database, IAM, etc. When I wrote this post, I made everything public to make it easy to test. My database was hacked with a ransom note saying I should pay 1 Bitcoin to recover it! Who would have thought &lt;code&gt;root&lt;/code&gt; and &lt;code&gt;password123&lt;/code&gt; as credentials is an open invitation to evil hackers?! 🤭&lt;/li&gt;
&lt;li&gt;You can find your Lambda logs in &lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;AWS CloudWatch&lt;/a&gt; of your chosen region&lt;/li&gt;
&lt;li&gt;If you want to add a new Lambda using this setup, you can create the Lambda in the &lt;code&gt;src/lambdas&lt;/code&gt; directory and update &lt;code&gt;serverless.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Always take care of your keys and secrets and do not store them in plain text in your app or config files.&lt;/li&gt;
&lt;li&gt;Always set &lt;code&gt;?connection_limit=1&lt;/code&gt; to the Prisma database URL if you are planning to use it in a Lambda to avoid exhausting the connection pool. Read more about the &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-client/deployment#recommended-connection-limit" rel="noopener noreferrer"&gt;recommended connection limit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ✌️ Summary
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed reading this post! If you have tips &amp;amp; tricks regarding any of the topics discussed or general feedback, don't be shy to send me a holler at &lt;a href="https://twitter.com/eddeee888" rel="noopener noreferrer"&gt;@eddeee888&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full repo with working CI/CD can be found here: &lt;a href="https://github.com/eddeee888/topic-prisma-aws-lambda-deployment" rel="noopener noreferrer"&gt;https://github.com/eddeee888/topic-prisma-aws-lambda-deployment&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>serverless</category>
      <category>prisma</category>
    </item>
    <item>
      <title>How to write a Bash shortcut script to enhance your terminal experience</title>
      <dc:creator>Eddy Nguyen</dc:creator>
      <pubDate>Tue, 26 Jan 2021 21:56:01 +0000</pubDate>
      <link>https://forem.com/eddeee888/how-to-write-a-bash-shortcut-script-to-enhance-your-terminal-experience-5898</link>
      <guid>https://forem.com/eddeee888/how-to-write-a-bash-shortcut-script-to-enhance-your-terminal-experience-5898</guid>
      <description>&lt;p&gt;I love shortcuts. If I had to pick the most impactful invention in human history, it would be Ctrl+C and Ctrl+V. We would still be living in caves if we didn't find ways to make our lives easier.&lt;/p&gt;

&lt;p&gt;Living in caves is what I felt about using the Terminal for a long time. Everything was so hard but felt so... magical at the same time. One day, I got tired of repeatedly bashing my keyboard in, putting down my club, learned Bash, and made my first script.&lt;/p&gt;

&lt;p&gt;Now, dear reader, gather around the fire (figuratively, of course), and let me tell you about my approach to writing a Bash script for shortcuts that got me from the stone age to 2021.&lt;/p&gt;

&lt;p&gt;(*) The commands you see in this post are executed on a Mac. If you are using Windows or Linux, you may have to adjust the commands slightly.&lt;/p&gt;

&lt;p&gt;(**) I'll stop mentioning cavemen.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧐 What the Bash?
&lt;/h2&gt;

&lt;p&gt;Bash is a command language that allows users to type in text commands to tell the computer to do specific actions. Mac and Linux usually come with Bash installed. &lt;/p&gt;

&lt;p&gt;If you go to &lt;strong&gt;Application&lt;/strong&gt; &amp;gt; &lt;strong&gt;Utilities&lt;/strong&gt; on a Mac, you will see an app called &lt;strong&gt;Terminal&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk4vqj6r3vwe2t4artrcu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk4vqj6r3vwe2t4artrcu.png" alt="Screen Shot 2021-01-22 at 11.41.58 pm"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Open the &lt;strong&gt;Terminal&lt;/strong&gt;, and you will see a black window with white texts - kinda like what the hackers use on TV. This is the command-line interface (CLI) that allows you to run text commands (including Bash commands and scripts).&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Starting with the basics
&lt;/h2&gt;

&lt;p&gt;You may have seen something like this before:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# This is an example, do not execute this command.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In this case, &lt;code&gt;yarn&lt;/code&gt; is the command name, and &lt;code&gt;install&lt;/code&gt; is the first parameter. We will want our Bash command to work in a similar way. For us, let's say if we execute the following command, it will open Netflix in the browser:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Hell yea - a shortcut to watch Netflix. I'm down!&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ss tv nf


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

&lt;/div&gt;

&lt;p&gt;(*) Executing a command means we type the command into the terminal and press return/enter.&lt;/p&gt;

&lt;p&gt;Let's create a new file with the following content:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#/bin/bash&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;ss&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello World"&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;0
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This file is called a script.&lt;/li&gt;
&lt;li&gt;The name of the function is &lt;code&gt;ss&lt;/code&gt; because we want the command name to be short ( it's a shortcut script after all! ). &lt;/li&gt;
&lt;li&gt;You can save this anywhere but I normally save all my custom scripts in &lt;code&gt;~/bin&lt;/code&gt;. For our script, we can save it as &lt;code&gt;~/bin/ss.sh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#/bin/bash&lt;/code&gt; is needed at the top so your operating system knows how to run this script i.e. using Bash.&lt;/li&gt;
&lt;li&gt;If this function is executed, it will print "Hello World" in the terminal.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return 0&lt;/code&gt; is how we tell the terminal that the command has run successfully.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚗 Executing the script
&lt;/h2&gt;

&lt;p&gt;Normally, if you try to execute the script by typing in the path to the script and hit return/enter, it will say that you don't have permission to do so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F44pxcaabupzxhy295wky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F44pxcaabupzxhy295wky.png" alt="Screen Shot 2021-01-21 at 10.17.42 pm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;😤 Hmm, I'm a grown-ass man. No one or nothing can "permission denied" me like this. Let's find a way to fix this.&lt;/p&gt;

&lt;p&gt;At this point, we could mess around with script permissions but I prefer sourcing the script for many reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need to update the script permission. 😌&lt;/li&gt;
&lt;li&gt;The function name i.e. &lt;code&gt;ss&lt;/code&gt; can be used as the command name. 🥰&lt;/li&gt;
&lt;li&gt;The script works regardless of its location. 😍&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can add the following line at the end of your &lt;code&gt;~/.zshrc&lt;/code&gt; or &lt;code&gt;~/.bashrc&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; ~/bin/ss.sh


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

&lt;/div&gt;

&lt;p&gt;Doing this will load the script into the terminals that you open in the future so you can execute the &lt;code&gt;ss&lt;/code&gt; command directly. &lt;/p&gt;

&lt;p&gt;Now, open a new terminal, type &lt;code&gt;ss&lt;/code&gt; into the command prompt, and hit return/enter, you will see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi0h7c7etf3zyq05rty1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi0h7c7etf3zyq05rty1m.png" alt="Screen Shot 2021-01-21 at 10.34.27 pm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ✂️ Creating shortcuts
&lt;/h2&gt;

&lt;p&gt;Now we get to the fun part! We want to check the parameters that come after the command name and assign them to an action we want to take. For example, we can update the script to something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#/bin/bash&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;ss&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      vi ~/bin/ss.sh
      &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="s2"&gt;"tv"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="s2"&gt;"nf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          open https://www.netflix.com/
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Opening Netflix"&lt;/span&gt;
          &lt;span class="k"&gt;return &lt;/span&gt;0
        &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="s2"&gt;"az"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          open https://www.primevideo.com/
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Opening Amazon Prime"&lt;/span&gt;
          &lt;span class="k"&gt;return &lt;/span&gt;0
        &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          open /System/Applications/TV.app
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Opening Apple TV"&lt;/span&gt;
          &lt;span class="k"&gt;return &lt;/span&gt;0
        &lt;span class="p"&gt;;;&lt;/span&gt;
      &lt;span class="k"&gt;esac&lt;/span&gt;

    &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;esac&lt;/span&gt;

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;ERROR - Invalid command&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;There's quite a bit to take in here but don't worry, here's how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are multiple &lt;a href="https://linuxize.com/post/bash-case-statement/" rel="noopener noreferrer"&gt;Bash case statements&lt;/a&gt;, each starting with &lt;code&gt;case&lt;/code&gt; and ending with &lt;code&gt;esac&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$1&lt;/code&gt; and &lt;code&gt;$2&lt;/code&gt; are the first parameter and second parameter (that comes after the script name) respectively.&lt;/li&gt;
&lt;li&gt;When executed, this function will check if the first parameter is &lt;code&gt;open&lt;/code&gt; or &lt;code&gt;tv&lt;/code&gt;. If it is &lt;code&gt;tv&lt;/code&gt;, it would go into its own case statement, step through the second parameter and check if it is &lt;code&gt;nf&lt;/code&gt; or &lt;code&gt;az&lt;/code&gt;. &lt;code&gt;*)&lt;/code&gt; is to catch all other options.&lt;/li&gt;
&lt;li&gt;If a correct combination is found, it will execute the code inside the respective block.&lt;/li&gt;
&lt;li&gt;If no combination is found, &lt;code&gt;return 1&lt;/code&gt; is executed to tell the terminal that an error occurred.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the first parameter is &lt;code&gt;open&lt;/code&gt;, we will use &lt;code&gt;vi&lt;/code&gt; command to make changes to the shortcut file. Remember to start a new terminal after making changes to load the new script!&lt;/li&gt;
&lt;li&gt;If the first parameter is &lt;code&gt;tv&lt;/code&gt; and the second parameter is &lt;code&gt;nf&lt;/code&gt;, it will open Nextflix in the default browser.&lt;/li&gt;
&lt;li&gt;If the first parameter is &lt;code&gt;tv&lt;/code&gt; and the second parameter is &lt;code&gt;az&lt;/code&gt;, it will open Amazon Prime in the default browser.&lt;/li&gt;
&lt;li&gt;If the first parameter is &lt;code&gt;tv&lt;/code&gt; and the second parameter is anything ( including no second parameter ), it will open the Apple TV app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try opening a new Bash script and executing the following command and see if it's working:&lt;/p&gt;

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

$ ss tv nf


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

&lt;/div&gt;

&lt;p&gt;If it opens Netflix in a new browser, congratulations! You have created your first Bash shortcut script. 🎉&lt;/p&gt;

&lt;p&gt;The above commands are pretty simple but you can add more commands in each block. In another script that I use, I start 5-6 web services locally on my laptop with one command. That is, like, 10000% productivity increase.. or something. Yea. 😎&lt;/p&gt;

&lt;p&gt;If you enter an invalid command, you will see an error message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmd57haop5yurkuu6fuyk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmd57haop5yurkuu6fuyk.png" alt="Screen Shot 2021-01-21 at 11.05.51 pm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🍕 BONUS - Add pizzazz to your script
&lt;/h2&gt;

&lt;p&gt;Error messages just have bad vibes. If I fail to type a command, I want encouragement from my computer, not getting yelled at! If you feel the same way, update the script to the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#/bin/bash&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;sayStuff&lt;span class="o"&gt;(){&lt;/span&gt;
  &lt;span class="nv"&gt;stuff_to_say&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"You may have a fat finger but you also have a freaking fat heart."&lt;/span&gt; &lt;span class="s2"&gt;"Take a deep breath and try again man. You got this!"&lt;/span&gt; &lt;span class="s2"&gt;"You will stop sucking one day."&lt;/span&gt; &lt;span class="s2"&gt;"Your command is invalid. But you are valid."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;stuff_to_say&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
  &lt;span class="nv"&gt;random&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="nv"&gt;random_stuff&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;stuff_to_say&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$random&lt;/span&gt;&lt;span class="p"&gt; % &lt;/span&gt;&lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="p"&gt; + 1]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;ERROR - Invalid command"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$random_stuff&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;ss&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="s2"&gt;"tv"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="s2"&gt;"nf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          open https://www.netflix.com/
          &lt;span class="k"&gt;return &lt;/span&gt;0
        &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="s2"&gt;"az"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          open https://www.primevideo.com/
          &lt;span class="k"&gt;return &lt;/span&gt;0
        &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          open /System/Applications/TV.app
          &lt;span class="k"&gt;return &lt;/span&gt;0
        &lt;span class="p"&gt;;;&lt;/span&gt;
      &lt;span class="k"&gt;esac&lt;/span&gt;

    &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      vi ~/bin/ss.sh
      &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;esac&lt;/span&gt;

  sayStuff
  &lt;span class="k"&gt;return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;sayStuff&lt;/code&gt; is a function that picks one of the few given quotes to cheer you up every time you enter an invalid shortcut combination. For me, some aggressive positivity is exactly what I want to hear during trying times:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"You may have a fat finger but you also have a freaking fat heart."&lt;/li&gt;
&lt;li&gt;"Take a deep breath and try again man. You got this!"&lt;/li&gt;
&lt;li&gt;"You will stop sucking one day."&lt;/li&gt;
&lt;li&gt;"Your command is invalid. But you are valid."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq2fuy9wy9vaznxgqe752.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq2fuy9wy9vaznxgqe752.png" alt="Screen Shot 2021-01-21 at 11.16.30 pm"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Writing a Bash shortcut script is a fun way to get used to scripting as well as saving yourself from typing too much ( everyone hates that right? ). Do you have other ways that make your daily workflows easier? I would love to hear from you. 🙂&lt;/p&gt;

&lt;p&gt;The full code example is available on GitHub: &lt;a href="https://github.com/eddeee888/topic-simple-bash" rel="noopener noreferrer"&gt;https://github.com/eddeee888/topic-simple-bash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bash</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to write tests for Prisma with Docker and Jest</title>
      <dc:creator>Eddy Nguyen</dc:creator>
      <pubDate>Mon, 11 Jan 2021 00:55:35 +0000</pubDate>
      <link>https://forem.com/eddeee888/how-to-write-tests-for-prisma-with-docker-and-jest-593i</link>
      <guid>https://forem.com/eddeee888/how-to-write-tests-for-prisma-with-docker-and-jest-593i</guid>
      <description>&lt;p&gt;I have been using &lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt; for a couple of my projects and I'm absolutely loving it. With generated types and easy to use API, I can effortlessly build features without having to worry about the data shapes anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌴 Trouble in ( testing ) paradise
&lt;/h2&gt;

&lt;p&gt;One small problem with Prisma is that it is not always clear how to write unit and functional tests. &lt;code&gt;.env&lt;/code&gt; file is used by default but it takes a bit of work to get &lt;code&gt;.env.test&lt;/code&gt; working as mentioned in this &lt;a href="https://github.com/prisma/prisma/issues/1519" rel="noopener noreferrer"&gt;issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; is great to separate development and testing environment. With Docker, &lt;code&gt;.env&lt;/code&gt; files are not needed because environment variables can be set when the containers are created. Since I was using Docker for development already, setting up a testing environment was very easy.&lt;/p&gt;

&lt;p&gt;In this post, I will talk about my approach to writing tests for Prisma-integrated applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚡ TLDR;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create and run tests in Docker containers.&lt;/li&gt;
&lt;li&gt;Set up and reset the database before and after tests.&lt;/li&gt;
&lt;li&gt;For unit tests, create a Prisma client and disconnect after each test.&lt;/li&gt;
&lt;li&gt;For functional tests, start a server and close it after each test.&lt;/li&gt;
&lt;li&gt;Full example with working CI here: &lt;a href="https://github.com/eddeee888/topic-prisma-testing" rel="noopener noreferrer"&gt;https://github.com/eddeee888/topic-prisma-testing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  NPM Packages
&lt;/h3&gt;

&lt;p&gt;First, let's install the npm packages that we need. Run this in your host terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn &lt;span class="nt"&gt;-D&lt;/span&gt; @prisma/cli @prisma/client @types/jest jest node-fetch ts-jest ts-node typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prisma schema
&lt;/h3&gt;

&lt;p&gt;Let's get started with a very simple Prisma schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ./src/prisma/schema.prisma
datasource db {
  provider = "mysql"
  url      = env("PRISMA_DATABASE_URL")
}

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

model User {
  id    Int    @id @default(autoincrement())
  email String @unique
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use &lt;code&gt;env("PRISMA_DATABASE_URL")&lt;/code&gt; for the &lt;code&gt;url&lt;/code&gt; because we will give it different values based on whether we are in a testing or development environment. &lt;/li&gt;
&lt;li&gt;A user's email is also unique so Prisma should throw an error if we try to add two users with the same email&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  App Docker container
&lt;/h3&gt;

&lt;p&gt;We will need a Node container to run migrations and tests in. We do this in containers so the environment is consistent for everyone - no more "but it works on my machine" problems!&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; to store what we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ./Dockerfile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:12.18.0-alpine3.11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="se"&gt;\ &lt;/span&gt;
  &amp;amp;&amp;amp; apk add bash \
  &amp;amp;&amp;amp; rm -rf /var/cache/apk/*
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . . &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker-compose
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;docker-compose&lt;/a&gt; is a tool to manage multi-container apps. In our case, we will need something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ./docker-compose.test.yml&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.7"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;."&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;base&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;SERVER_DATABASE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test_db&lt;/span&gt;
      &lt;span class="na"&gt;PRISMA_DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql://root:root@database:3306/test_db?schema=public&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9999:80&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./src:/usr/src/app/src&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./package.json:/usr/src/app/package.json&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test_vm&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database&lt;/span&gt;

  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:5.7&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD=root&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_PORT=3306&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database:/var/lib/mysql&lt;/span&gt;
    &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;3307&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3307:3306&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test_vm&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test_vm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file above is quite long but don't fret! The most important things to note here are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are 2 services: &lt;code&gt;server&lt;/code&gt; and &lt;code&gt;database&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt; which is a server with node v12.18.0 ( and a few other things installed as stated in the Dockerfile above )&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt; has &lt;code&gt;PRISMA_DATABASE_URL&lt;/code&gt; set, which means it can run Prisma commands against the database.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;database&lt;/code&gt; is a mysql database ( which matches Prisma schema ).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧑‍🍳 Prepare the testing environment
&lt;/h2&gt;

&lt;p&gt;Let's start by building our Node image. We will use this image to manage migrations for the test database.&lt;/p&gt;

&lt;p&gt;Run the following command on your host terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml build &lt;span class="nt"&gt;--no-cache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check if your image has been built successfully by running the &lt;code&gt;docker images&lt;/code&gt; command. It will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fot7947e5ora5hbf3s241.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fot7947e5ora5hbf3s241.png" alt="Screen Shot 2021-01-10 at 8.31.04 pm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's create a new migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml run &lt;span class="nt"&gt;--rm&lt;/span&gt; server yarn prisma migrate save &lt;span class="nt"&gt;--experimental&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; add-user-model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we apply the migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml run &lt;span class="nt"&gt;--rm&lt;/span&gt; server yarn prisma migrate up &lt;span class="nt"&gt;--experimental&lt;/span&gt; &lt;span class="nt"&gt;--create-db&lt;/span&gt; &lt;span class="nt"&gt;--auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 Unit tests
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Writing unit tests
&lt;/h3&gt;

&lt;p&gt;We can't run tests unless we write a function to test first 😛. Let's add a simple function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./src/actions/createUserAction.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CreateUserActionParams&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUserAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserActionParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;createUserAction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a very contrived example that just calls Prisma functions underneath. The thing to note here is that a Prisma client is injected from the callsite to make it easy to test.&lt;/p&gt;

&lt;p&gt;We will need to install the following packages to generate unique emails for our tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn add -D uuid @types/uuid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's our test file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./src/actions/createUserAction.test.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createUserAction&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./createUserAction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createUserAction() - unit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;creates new user correctly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@test.com`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createUserAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;savedUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fails if tries to create records with the same user twice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@test.com`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createUserAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;savedUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createUserAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unique constraint failed on the constraint: `email_unique`&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, let's inspect the important parts of this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we create a new client for this test file ( and other files too ). This is fairly inexpensive so we can run it for every file. After all of the tests in this file, we will disconnect the Prisma client from the database to avoid hogging connections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;creates new user correctly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@test.com`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createUserAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;savedUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this test, we create a user with a unique email and make sure we can query it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fails if tries to create records with the same user twice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@test.com`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createUserAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;savedUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createUserAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unique constraint failed on the constraint: `email_unique`&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this above test, we test that if we try to create a user with the same email, it will throw an error the second time!&lt;/p&gt;

&lt;h3&gt;
  
  
  Running tests
&lt;/h3&gt;

&lt;p&gt;Finally, here's the moment we are all waiting for. Let's run the tests!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml run &lt;span class="nt"&gt;--rm&lt;/span&gt; server yarn jest &lt;span class="nt"&gt;-i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;code&gt;-i&lt;/code&gt; flag is used to make sure we run tests one by one to avoid race conditions in tests.&lt;/p&gt;

&lt;p&gt;Sometimes, our tests may fail because the database container is not ready before tests are run. It is highly recommended to be using something like &lt;a href="https://github.com/vishnubob/wait-for-it/blob/master/wait-for-it.sh" rel="noopener noreferrer"&gt;wait-for-it.sh&lt;/a&gt;. We can copy the file into &lt;code&gt;./scripts/wait-for-it.sh&lt;/code&gt;. Then, we can run the following instead of the previous command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml run &lt;span class="nt"&gt;--rm&lt;/span&gt; server ./scripts/wait-for-it.sh database:3306 &lt;span class="nt"&gt;--&lt;/span&gt; yarn jest &lt;span class="nt"&gt;-i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚗 Functional tests
&lt;/h2&gt;

&lt;p&gt;Functional tests are specifications of how a system works. For example, if our app receives a request at a certain URL, a new user is created. &lt;/p&gt;

&lt;p&gt;Let's create an app server. First, we need to install a few packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;types&lt;/span&gt;&lt;span class="sr"&gt;/express node-fetch @types/&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can create a server. Note that we don't start the server yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./src/createServer.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Express&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createUserAction&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./actions/createUserAction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CreateServerParams&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;CreateServerParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Express&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/new-user/:email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createUserAction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Cannot create new user for email: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In here, our &lt;code&gt;createServer&lt;/code&gt; function also takes a Prisma client to make it easier to test. If a GET request is sent to &lt;code&gt;/new-user/:email&lt;/code&gt; ( e.g. &lt;code&gt;http://website.com/new-user/cool.personl@zmail.com&lt;/code&gt; ), then we will call &lt;code&gt;createUserAction&lt;/code&gt; to create a new user and send back 200 if is successful or 403 if encountered errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Please DO NOT - I REPEAT, DO NOT - have a URL that can create new users on GET requests without input validation/authentication/authorization, etc. or you will get an army of angry pelicans delivering spams to your app! ☠️&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing functional tests
&lt;/h3&gt;

&lt;p&gt;Now, we can start a new server for our tests to run against:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./src/actions/createUserAction.functional.test.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createServer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./createServer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;internalConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;internalConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;internalConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createUserAction() - functional&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;creates new user correctly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@test.com`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`http://localhost/new-user/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fails if tries to create records with the same user twice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@test.com`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`http://localhost/new-user/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, let's break this down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;internalConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;internalConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;internalConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This snippet of code creates a new Prisma client for the server. Before the tests in this file start, start the server at port 80. After the tests in this file end, stop the server and disconnect Prisma client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;creates new user correctly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@test.com`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`http://localhost/new-user/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above test, we send a request to our server, and if it is a new user, then it's all g!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fails if tries to create records with the same user twice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;@test.com`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`http://localhost/new-user/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the second test, we are trying to create a user who already exists, which causes the response to fail. Perfect! 🕺&lt;/p&gt;

&lt;p&gt;Then, we can run the same test command again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml run &lt;span class="nt"&gt;--rm&lt;/span&gt; server ./scripts/wait-for-it.sh database:3306 &lt;span class="nt"&gt;--&lt;/span&gt; yarn jest &lt;span class="nt"&gt;-i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Testing Prisma is not simple because it is hard to separate an environment for testing. Using Docker solves this issue for me. Do you know of a different way to test Prisma? I would love to hear from you 😊&lt;/p&gt;

&lt;p&gt;For the full development and test environment examples, including CI ( GitHub actions ), check out this repository: &lt;a href="https://github.com/eddeee888/topic-prisma-testing" rel="noopener noreferrer"&gt;https://github.com/eddeee888/topic-prisma-testing&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>docker</category>
      <category>prisma</category>
    </item>
  </channel>
</rss>
