<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[@Tech.hebdo]]></title><description><![CDATA[A fullstack Software engineer. I write here about my learnings and experiences.]]></description><link>https://blog.foujeupavel.com/</link><image><url>https://blog.foujeupavel.com/favicon.png</url><title>@Tech.hebdo</title><link>https://blog.foujeupavel.com/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Tue, 14 Apr 2026 02:33:59 GMT</lastBuildDate><atom:link href="https://blog.foujeupavel.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Designing Scalable, Reliable Backend Systems]]></title><description><![CDATA[<p>Backend development at scale is no longer about frameworks, endpoints, or writing code that works. At an advanced level, backend development is not about frameworks, syntax, or clever abstractions. It&#x2019;s about <strong>designing resilient, scalable, maintainable systems</strong> that survive reality: traffic spikes, network failures, partial outages, human mistakes, and</p>]]></description><link>https://blog.foujeupavel.com/the-advanced-backend-developer-guide/</link><guid isPermaLink="false">6973bbc3b222eca3af4052e4</guid><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Fri, 23 Jan 2026 19:40:06 GMT</pubDate><media:content url="https://blog.foujeupavel.com/content/images/2026/01/People_in_programming_01ung_03.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.foujeupavel.com/content/images/2026/01/People_in_programming_01ung_03.jpg" alt="Designing Scalable, Reliable Backend Systems"><p>Backend development at scale is no longer about frameworks, endpoints, or writing code that works. At an advanced level, backend development is not about frameworks, syntax, or clever abstractions. It&#x2019;s about <strong>designing resilient, scalable, maintainable systems</strong> that survive reality: traffic spikes, network failures, partial outages, human mistakes, and evolving teams.</p><p>Having led and designed systems that have scaled from hundreds to millions of users, I want to share the patterns, trade-offs, and concrete examples that make the differences. </p><p>This guide documents the principles to build <strong>scalable, resilient, production-grade systems</strong>.</p><h2 id="1-systems-design-over-endpoints-%E2%80%94-real-signup-flow-example">1. Systems Design Over Endpoints &#x2014; Real Signup Flow Example</h2><p>Junior devs think API endpoints. Leads think flows and failure modes.</p><h3 id="scenario-signup-flow-with-external-dependencies">Scenario: Signup Flow with External Dependencies</h3><p><strong>Requirements:</strong></p><ul><li>Register user credentials</li><li>Send welcome email</li><li>Log analytics</li><li>Create billing profile</li><li>Enable feature flags</li></ul><h3 id="naive-synchronous-design">Naive Synchronous Design</h3><pre><code class="language-javascript">POST /signup
  &#x2192; create user record
  &#x2192; send welcome email (blocking)
  &#x2192; create billing profile (blocking)
  &#x2192; log analytics
  &#x2192; enable feature flags
  &#x2192; respond 200 OK
</code></pre><p><strong>Why this is bad:</strong></p><ul><li>If the email service is down, signup fails.</li><li>Latency depends on the slowest external call.</li><li>System is tightly coupled and hard to scale.</li><li>No clear failure isolation.</li></ul><hr><h3 id="lead-level-design-asynchronous-event-driven-resilient">Lead-Level Design: Asynchronous, Event-Driven, Resilient</h3><p><strong>Architecture diagram description:</strong></p><ul><li>Client calls <code>POST /signup</code> &#x2192; User Service creates user record with status <code>PENDING</code>.</li><li>User Service emits a <code>UserCreated</code> event on an Event Bus (Kafka, RabbitMQ, or similar).</li><li>Email Service asynchronously consumes <code>UserCreated</code> and sends a welcome email.</li><li>Billing Service consumes the event and creates a billing profile.</li><li>Analytics Service consumes the event to log metrics.</li><li>Feature Flags Service listens and enables features.</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.07.04-PM.png" class="kg-image" alt="Designing Scalable, Reliable Backend Systems" loading="lazy" width="1215" height="424" srcset="https://blog.foujeupavel.com/content/images/size/w600/2026/01/Capture-d-e-cran-2026-01-23-a--8.07.04-PM.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2026/01/Capture-d-e-cran-2026-01-23-a--8.07.04-PM.png 1000w, https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.07.04-PM.png 1215w" sizes="(min-width: 720px) 720px"></figure><p><strong>Explanation:</strong></p><ul><li><code>POST /signup</code> writes user record synchronously.</li><li>Emits a <code>UserCreated</code> event on Event Bus (Kafka/RabbitMQ).</li><li>Other services consume events asynchronously and retry on failure.</li></ul><p><strong>Code snippet &#x2014; event emission in Node.js:</strong></p><pre><code class="language-typesscript">// userService.js
async function signupUser(userData) {
  const user = await UserModel.create(userData);
  await eventBus.publish(&apos;UserCreated&apos;, { userId: user.id, email: user.email });
  return user;
}
</code></pre><p><strong>Tooling suggestion:</strong></p><ul><li>Use Kafka or RabbitMQ for reliable event streaming.</li><li>Implement dead-letter queues (DLQs) for failed events.</li></ul><h2 id="2-data-modeling-for-reality-%E2%80%94-orders-system-example">2. Data Modeling for Reality &#x2014; Orders System Example</h2><h3 id="naive-schema-normalized">Naive schema (normalized):</h3><p>Tables:</p><ul><li><code>orders</code> (id, user_id, total, status)</li><li><code>order_items</code> (id, order_id, product_id, quantity)</li><li><code>products</code> (id, name, price)</li></ul><h3 id="problem-query">Problem query:</h3><p>Get total revenue per day &#x2192; expensive joins and aggregations.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.11.48-PM.png" class="kg-image" alt="Designing Scalable, Reliable Backend Systems" loading="lazy" width="967" height="519" srcset="https://blog.foujeupavel.com/content/images/size/w600/2026/01/Capture-d-e-cran-2026-01-23-a--8.11.48-PM.png 600w, https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.11.48-PM.png 967w" sizes="(min-width: 720px) 720px"></figure><p><strong>Explanation:</strong></p><ul><li>Write model normalized for ACID.</li><li>Event processor updates denormalized aggregates asynchronously.</li><li>Dashboard queries aggregates, avoiding expensive joins.</li></ul><p><strong>Code snippet &#x2014; event processor updating aggregates:</strong></p><pre><code class="language-typescript">eventBus.subscribe(&apos;OrderCreated&apos;, async (event) =&gt; {
  const { date, total } = event.payload;
  await AggregatesDB.query(`
    INSERT INTO daily_order_stats(date, total_revenue, order_count)
    VALUES ($1, $2, 1)
    ON CONFLICT (date)
    DO UPDATE SET
      total_revenue = daily_order_stats.total_revenue + $2,
      order_count = daily_order_stats.order_count + 1
  `, [date, total]);
});</code></pre><p>3. Transaction Boundaries &amp; Consistency &#x2014; Payment Flow</p><pre><code class="language-typescript">POST /checkout
  &#x2192; create order
  &#x2192; charge card (external API)
  &#x2192; mark order paid</code></pre><p><strong>Problem:</strong> </p><ul><li>Failures cause duplicate charges or lost updates.</li><li>If payment gateway succeeds but response is lost, system inconsistencies arise.</li></ul><h3 id="robust-event-driven-flow">Robust Event-Driven Flow</h3><p><strong>Steps:</strong></p><ol><li>Client calls <code>/checkout</code> &#x2192; Order Service creates order with <code>PENDING</code> status.</li></ol><p>2. Order Service emits <code>PaymentRequested</code> event.</p><p>3. Payment Service listens:</p><ul><li>Charges card.</li><li>Emits <code>PaymentSucceeded</code> or <code>PaymentFailed</code>.</li></ul><p>4. Order Service updates order status accordingly.</p><p><strong>Idempotency:</strong></p><ul><li>Payment Service tracks transaction IDs.</li><li>Duplicate events are ignored.</li><li>Webhook duplicates handled safely.</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.13.01-PM.png" class="kg-image" alt="Designing Scalable, Reliable Backend Systems" loading="lazy" width="1620" height="340" srcset="https://blog.foujeupavel.com/content/images/size/w600/2026/01/Capture-d-e-cran-2026-01-23-a--8.13.01-PM.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2026/01/Capture-d-e-cran-2026-01-23-a--8.13.01-PM.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2026/01/Capture-d-e-cran-2026-01-23-a--8.13.01-PM.png 1600w, https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.13.01-PM.png 1620w" sizes="(min-width: 720px) 720px"></figure><h2 id="4-api-design-for-longevity-and-compatibility">4. API Design for Longevity and Compatibility</h2><p>APIs are contracts that live far longer than their creators.</p><h3 id="common-anti-patterns">Common anti-patterns:</h3><p>Breaking clients with minor changes</p><p>Ignoring backward compatibility</p><p>Versioning by URL path (<code>/v1</code>, <code>/v2</code>), leading to maintenance hell</p><h3 id="better-practices">Better Practices</h3><ul><li>Use media type versioning in headers (<code>Accept: application/vnd.myapi.v2+json</code>)</li><li>Add new fields; don&#x2019;t remove or rename existing ones.</li><li>Mark deprecated fields clearly.</li><li>Use feature flags to toggle behaviors.</li></ul><p>This lets you evolve APIs <strong>without breaking existing clients</strong>.</p><h2 id="5-asynchronous-architecture-event-thinking">5. Asynchronous Architecture &amp; Event Thinking</h2><p>Synchronous monoliths break under load and complexity.</p><h3 id="events-are-facts-about-what-happened">Events are facts about what happened.</h3><p>Example: Deleting a user:</p><ul><li>Emit <code>UserDeleted { user_id, timestamp }</code></li><li>Consumers revoke access, delete data, cancel subscriptions <strong>asynchronously</strong>.</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.22.38-PM.png" class="kg-image" alt="Designing Scalable, Reliable Backend Systems" loading="lazy" width="967" height="301" srcset="https://blog.foujeupavel.com/content/images/size/w600/2026/01/Capture-d-e-cran-2026-01-23-a--8.22.38-PM.png 600w, https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.22.38-PM.png 967w" sizes="(min-width: 720px) 720px"></figure><p>This ensures:</p><ul><li>The deletion API is fast and reliable.</li><li>Downstream systems can process independently.Retry and ordering issues can be handled per consumer.</li></ul><h2 id="6-caching-as-architecture-not-optimization">6. Caching as Architecture, Not Optimization</h2><p>Caching decisions require clear ownership.</p><p>Example: Dashboard Cache</p><ul><li>Use Redis or similar to cache dashboard query results for 5 minutes.</li><li>Cache invalidation triggered by write services emitting invalidation messages.</li><li>Avoid infinite TTLs or guesswork.</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.29.03-PM.png" class="kg-image" alt="Designing Scalable, Reliable Backend Systems" loading="lazy" width="235" height="444"></figure><h2 id="7-reliability-engineering-%E2%80%94-circuit-breaker-pattern">7. Reliability Engineering &#x2014; Circuit Breaker Pattern</h2><h3 id="problem">Problem:</h3><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.30.39-PM.png" class="kg-image" alt="Designing Scalable, Reliable Backend Systems" loading="lazy" width="812" height="226" srcset="https://blog.foujeupavel.com/content/images/size/w600/2026/01/Capture-d-e-cran-2026-01-23-a--8.30.39-PM.png 600w, https://blog.foujeupavel.com/content/images/2026/01/Capture-d-e-cran-2026-01-23-a--8.30.39-PM.png 812w" sizes="(min-width: 720px) 720px"></figure><p>Email service is flaky and causes request timeouts.</p><h3 id="solution">Solution:</h3><ul><li>Add a circuit breaker to email calls.</li><li>After 5 failures in 10 seconds, circuit opens.</li><li>Requests fail fast, emails queued for later.</li><li>Circuit resets when email service recovers.</li></ul><p>This prevents cascading failures and improves user experience.</p><h2 id="8-observability-%E2%80%94-distributed-tracing">8. Observability &#x2014; Distributed Tracing</h2><ul><li>Use OpenTelemetry to propagate trace IDs.</li><li>Each service logs structured spans with start/end times.</li><li>Visualization tools (Jaeger, Zipkin) show request flow and latency bottlenecks.</li></ul><hr><h2 id="9-performance-tail-latency">9. Performance &amp; Tail Latency</h2><ul><li>Track latency percentiles (P50, P95, P99).</li><li>Identify tail latency sources: slow DB queries, network timeouts.</li><li>Use timeouts, circuit breakers, caching to reduce tail effects.</li></ul><hr><h2 id="10-security-as-architecture-%E2%80%94-zero-trust-model">10. Security as Architecture &#x2014; Zero Trust Model</h2><ul><li>Mutual TLS between services.</li><li>JWT-based auth and authorization for every internal call.</li><li>Audit logs for sensitive operations.</li></ul><hr><h2 id="11-cost-aware-design-%E2%80%94-aws-lambda-example">11. Cost-Aware Design &#x2014; AWS Lambda Example</h2><ul><li>Combine small functions to reduce invocation count.</li><li>Use provisioned concurrency for critical flows.</li><li>Analyze cost per request and optimize.</li></ul><hr><h2 id="12-maintainability-over-cleverness">12. Maintainability Over Cleverness</h2><ul><li>Favor explicit, readable code.</li><li>Avoid abstractions that obscure flow.</li><li>Write code new engineers can understand in days, not weeks.</li></ul><hr><h2 id="13-senior-lead-and-staff-engineer-differences">13. Senior, Lead, and Staff Engineer Differences</h2><ul><li><strong>Senior:</strong> solves problems reliably.</li><li><strong>Lead:</strong> designs systems and teams that prevent problems.</li><li><strong>Staff:</strong> aligns architecture and multiplies impact organization-wide.</li></ul><hr><h1 id="final-words">Final Words</h1><p>Building production-grade backends is a craft of <strong>judgment under uncertainty</strong>.</p><p>Mastering these patterns and tools positions you to design systems that:</p><ul><li>Fail gracefully</li><li>Scale predictably</li><li>Are maintainable and observable</li></ul>]]></content:encoded></item><item><title><![CDATA[Applying SOLID Principles in TypeScript with Node.js and Apollo Server: A Real-World GraphQL Example 🚀]]></title><description><![CDATA[<p>In the world of software development, writing maintainable and scalable code is crucial for long-term success. The SOLID principles, a set of five design principles, are designed to help developers achieve these goals by promoting good software design practices. In this article, we&apos;ll explore how to apply the</p>]]></description><link>https://blog.foujeupavel.com/applying-solid-principles-in-typescript-with-node-js-and-apollo-server-a-real-world-graphql-example/</link><guid isPermaLink="false">66d4ec92a387ca05b9a2d962</guid><category><![CDATA[Best Practices]]></category><category><![CDATA[Backend]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Typescript]]></category><category><![CDATA[Graphql]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Mon, 02 Sep 2024 00:20:40 GMT</pubDate><media:content url="https://blog.foujeupavel.com/content/images/2024/09/solid.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.foujeupavel.com/content/images/2024/09/solid.png" alt="Applying SOLID Principles in TypeScript with Node.js and Apollo Server: A Real-World GraphQL Example &#x1F680;"><p>In the world of software development, writing maintainable and scalable code is crucial for long-term success. The SOLID principles, a set of five design principles, are designed to help developers achieve these goals by promoting good software design practices. In this article, we&apos;ll explore how to apply the SOLID principles in a real-world Node.js application using TypeScript and Apollo Server to build a GraphQL API.</p><p>This article is divided into several sections:</p><ol><li><strong>Introduction to SOLID Principles</strong></li><li><strong>Setting Up the Project</strong></li><li><strong>Single Responsibility Principle (SRP)</strong></li><li><strong>Open/Closed Principle (OCP)</strong></li><li><strong>Liskov Substitution Principle (LSP)</strong></li><li><strong>Interface Segregation Principle (ISP)</strong></li><li><strong>Dependency Inversion Principle (DIP)</strong></li><li><strong>Conclusion</strong></li></ol><h2 id="1-introduction-to-solid-principles">1. Introduction to SOLID Principles</h2><p>SOLID is an acronym for five design principles that aim to make software more understandable, flexible, and maintainable. Here&apos;s a quick overview:</p><ul><li><strong>Single Responsibility Principle (SRP):</strong> A class should have only one reason to change, meaning it should have only one job or responsibility.</li><li><strong>Open/Closed Principle (OCP):</strong> Software entities should be open for extension but closed for modification.</li><li><strong>Liskov Substitution Principle (LSP):</strong> Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.</li><li><strong>Interface Segregation Principle (ISP):</strong> No client should be forced to depend on methods it does not use.</li><li><strong>Dependency Inversion Principle (DIP):</strong> High-level modules should not depend on low-level modules. Both should depend on abstractions.</li></ul><p>In this article, we&apos;ll implement a GraphQL API using Apollo Server and TypeScript, applying these principles to create a robust and scalable architecture.</p><h2 id="2-setting-up-the-project">2. Setting Up the Project</h2><p>Before applying the SOLID principles, let&apos;s develop a basic Node.js project with TypeScript and Apollo Server.</p><h3 id="step-1-initialize-the-project">step 1: Initialize the project</h3><p>On the terminal, execute the following command: </p><pre><code class="language-bash">mkdir solid-graphql &amp;&amp; cd solid-graphql &amp;&amp; npm init -y</code></pre><h3 id="step-2-install-dependencies">step 2: Install dependencies</h3><p>On the terminal, execute the following command: </p><pre><code class="language-bash">npm install apollo-server graphql reflect-metadata type-graphql 
npm install --save-dev typescript ts-node nodemon @types/node</code></pre><h3 id="step-3-configure-typescript">step 3: Configure TypeScript</h3><p>Create a <code>tsconfig.json</code> file:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;ES2020&quot;,
    &quot;module&quot;: &quot;commonjs&quot;,
    &quot;strict&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;outDir&quot;: &quot;./dist&quot;
  },
  &quot;include&quot;: [&quot;src&quot;],
  &quot;exclude&quot;: [&quot;node_modules&quot;]
}
</code></pre><figcaption>tsconfig.json</figcaption></figure><h3 id="step-4-create-the-project-structure">step 4: Create the project structure</h3><pre><code class="language-bash">mkdir src &amp;&amp; touch src/index.ts</code></pre><h3 id="step-5-set-up-apollo-server">step 5: Set up Apollo Server</h3><p>Let&apos;s start by setting up a basic Apollo Server in <code>src/index.ts</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import &apos;reflect-metadata&apos;;
import { ApolloServer } from &apos;apollo-server&apos;;
import { buildSchema } from &apos;type-graphql&apos;;

async function startServer() {
  const schema = await buildSchema({
    // Resolvers will be added here
  });

  const server = new ApolloServer({ schema });

  server.listen({ port: 4000 }, () =&gt;
    console.log(&apos;Server is running on http://localhost:4000/graphql&apos;)
  );
}

startServer();</code></pre><figcaption>index.ts</figcaption></figure><p></p><p>With this setup, we&apos;re ready to start applying the SOLID principles. Let&apos;s Go &#x1F9D1;&#x200D;&#x1F4BB;</p><h2 id="3-single-responsibility-principle-srp">3. Single Responsibility Principle (SRP)</h2><p>The Single Responsibility Principle states that a class should have only one reason to change. In the context of our GraphQL API, this means that each part of our application should be responsible for a single aspect of functionality. Let&apos;s implement a simple user management feature to demonstrate SRP.</p><h3 id="example-user-entity-and-resolver">Example: User Entity and Resolver</h3><p>Let&apos;s create a &#xA0;<code>User</code> entity and its corresponding resolver.</p><h4 id="step-1-create-the-user-entity">Step 1: Create the User Entity</h4><pre><code class="language-bash">mkdir -p src/entities
touch src/entities/User.ts
</code></pre><h3 id="step-2-create-the-user-resolver">Step 2: Create the User Resolver</h3><pre><code class="language-bash">mkdir -p src/resolvers
touch src/resolvers/UserResolver.ts
</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import { Query, Resolver } from &apos;type-graphql&apos;;
import { User } from &apos;../entities/User&apos;;

@Resolver(User)
export class UserResolver {
  private users: User[] = [
    { id: &apos;1&apos;, name: &apos;John Doe&apos;, email: &apos;john@example.com&apos; },
    { id: &apos;2&apos;, name: &apos;Jane Smith&apos;, email: &apos;jane@example.com&apos; },
  ];

  @Query(() =&gt; [User])
  getUsers(): User[] {
    return this.users;
  }
}
</code></pre><figcaption>UserResolver.ts</figcaption></figure><h4 id="step-3-register-the-resolver">Step 3: Register the Resolver</h4><p>Update the <code>src/index.ts</code> file to include the resolver:</p><pre><code>import { UserResolver } from &apos;./resolvers/UserResolver&apos;;

const schema = await buildSchema({
  resolvers: [UserResolver],
});
</code></pre><h3 id="srp-in-action">SRP in Action</h3><p>In this example, the <code>User</code> entity is only responsible for defining the structure of a user, and the <code>UserResolver</code> is responsible for handling GraphQL queries related to users. This separation ensures that changes to how we fetch or modify users will not affect the user structure, adhering to SRP. Each class has a single responsibility, making the code easier to maintain.</p><h3 id="srp-violation-example">SRP Violation Example</h3><p>Now, let&apos;s see how violating SRP can cause issues. Imagine we decide to add user logging functionality directly into the <code>UserResolver</code>.</p><pre><code class="language-javascript">import { Query, Resolver } from &apos;type-graphql&apos;;
import { User } from &apos;../entities/User&apos;;

@Resolver(User)
export class UserResolver {
  private users: User[] = [
    { id: &apos;1&apos;, name: &apos;John Doe&apos;, email: &apos;john@example.com&apos; },
    { id: &apos;2&apos;, name: &apos;Jane Smith&apos;, email: &apos;jane@example.com&apos; },
  ];

  @Query(() =&gt; [User])
  getUsers(): User[] {
    this.logUsers(); // Added logging functionality
    return this.users;
  }

  private logUsers() {
    console.log(&apos;Users:&apos;, this.users);
  }
}
</code></pre><h4 id="why-this-violates-srp">Why This Violates SRP</h4><p>The <code>UserResolver</code> now has two responsibilities: fetching users and logging them. This coupling can make the code harder to change and test. If the logging requirement changes, you&apos;ll need to modify the <code>UserResolver</code>, which violates SRP.</p><p><strong>Solution:</strong> Create a separate <code>UserLogger</code> class that handles logging:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">class UserLogger {
  log(users: User[]) {
    console.log(&apos;Users:&apos;, users);
  }
}
</code></pre><figcaption>UserLogger.ts</figcaption></figure><p>Then use it in the <code>UserResolver</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">@Resolver(User)
export class UserResolver {
  private userLogger = new UserLogger();

  @Query(() =&gt; [User])
  getUsers(): User[] {
    this.userLogger.log(this.users);
    return this.users;
  }
}
</code></pre><figcaption>UserResolver.ts</figcaption></figure><p>This way, the <code>UserResolver</code> remains focused on its primary responsibility.</p><h2 id="4-openclosed-principle-ocp">4. Open/Closed Principle (OCP)</h2><h3 id="ocp-in-action">OCP in Action</h3><p>The <strong>Open/Closed Principle</strong> suggests that software entities should be open for extension but closed for modification. Let&apos;s extend the <code>UserResolver</code> to support filtering users by name without modifying the existing <code>getUsers</code> method.</p><h4 id="step-1-add-a-new-query">Step 1: Add a New Query</h4><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import { Arg, Query } from &apos;type-graphql&apos;;

@Resolver(User)
export class UserResolver {
  private users: User[] = [
    { id: &apos;1&apos;, name: &apos;John Doe&apos;, email: &apos;john@example.com&apos; },
    { id: &apos;2&apos;, name: &apos;Jane Smith&apos;, email: &apos;jane@example.com&apos; },
  ];

  @Query(() =&gt; [User])
  getUsers(): User[] {
    return this.users;
  }

  @Query(() =&gt; [User])
  filterUsersByName(@Arg(&apos;name&apos;) name: string): User[] {
    return this.users.filter(user =&gt; user.name.includes(name));
  }
}
</code></pre><figcaption>UserResolver.ts</figcaption></figure><h3 id="ocp-violation-example">OCP Violation Example</h3><p>Now, let&apos;s violate the OCP by directly modifying the <code>getUsers</code> method to include filtering.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">@Query(() =&gt; [User])
getUsers(@Arg(&apos;name&apos;, { nullable: true }) name?: string): User[] {
  if (name) {
    return this.users.filter(user =&gt; user.name.includes(name));
  }
  return this.users;
}
</code></pre><figcaption>UserResolver.ts</figcaption></figure><h4 id="why-this-violates-ocp">Why This Violates OCP</h4><p>By modifying the <code>getUsers</code> method to include filtering logic, we are changing the existing functionality. This violates OCP because any changes to the filtering logic could potentially break the original <code>getUsers</code> method.</p><p><strong>Solution:</strong> Instead of modifying the existing method, we should add a new method (like <code>filterUsersByName</code>) that extends the functionality while keeping the original method unchanged.</p><h2 id="5-liskov-substitution-principle-lsp">5. Liskov Substitution Principle (LSP)</h2><h3 id="lsp-in-action">LSP in Action</h3><p>The <strong>Liskov Substitution Principle</strong> states that objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. Let&apos;s create a <code>SpecialUser</code> class that extends the <code>User</code> class.</p><h4 id="step-1-create-the-specialuser-class">Step 1: Create the SpecialUser Class</h4><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import { Field, ObjectType } from &apos;type-graphql&apos;;
import { User } from &apos;./User&apos;;

@ObjectType()
export class SpecialUser extends User {
  @Field()
  isAdmin: boolean;
}
</code></pre><figcaption>SpecialUser.ts</figcaption></figure><h4 id="step-2-update-the-resolver">Step 2: Update the Resolver</h4><figure class="kg-card kg-code-card"><pre><code class="language-javascript">@Resolver(User)
export class UserResolver {
  private users: User[] = [
    { id: &apos;1&apos;, name: &apos;John Doe&apos;, email: &apos;john@example.com&apos; },
    { id: &apos;2&apos;, name: &apos;Jane Smith&apos;, email: &apos;jane@example.com&apos; },
  ];

  @Query(() =&gt; [User])
  getUsers(): User[] {
    return this.users;
  }

  @Query(() =&gt; [SpecialUser])
  getSpecialUsers(): SpecialUser[] {
    return this.users.map(user =&gt; ({
      ...user,
      isAdmin: false,
    })) as SpecialUser[];
  }
}
</code></pre><figcaption>UserResolver.ts</figcaption></figure><h3 id="lsp-violation-example">LSP Violation Example</h3><p>Let&apos;s violate the LSP by changing the behavior of the subclass in a way that breaks the expected behavior.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">@ObjectType()
export class SpecialUser extends User {
  @Field()
  isAdmin: boolean;

  // Overriding a method to break LSP
  getUserEmail() {
    throw new Error(&apos;Email is restricted&apos;);
  }
}
</code></pre><figcaption>SpecialUser.ts</figcaption></figure><h4 id="why-this-violates-lsp">Why This Violates LSP</h4><p>The <code>getUserEmail</code> method in <code>SpecialUser</code> behaves differently from what is expected in the <code>User</code> class. If we substitute a <code>User</code> object with a <code>SpecialUser</code> object, the application will break when trying to access the user&apos;s email. This violates LSP because the subclass does not adhere to the contract established by the superclass.</p><p><strong>Solution:</strong> Ensure that the subclass does not change the expected behavior of the superclass. Any new functionality should be additive and not modify the existing contract.</p><h2 id="6-interface-segregation-principle-isp">6. Interface Segregation Principle (ISP)</h2><h3 id="isp-in-action">ISP in Action</h3><p>The <strong>Interface Segregation Principle</strong> states that no client should be forced to depend on methods it does not use. Let&apos;s create interfaces for different user services.</p><h4 id="step-1-define-interfaces">Step 1: Define Interfaces</h4><figure class="kg-card kg-code-card"><pre><code class="language-javascript">// src/services/IUserService.ts
export interface IUserService {
  getUsers(): User[];
  getUserById(id: string): User | undefined;
}

// src/services/IAdminService.ts
export interface IAdminService {
  promoteToAdmin(id: string): void;
}
</code></pre><figcaption>IUserService.ts</figcaption></figure><h3 id="isp-violation-example">ISP Violation Example</h3><p>Let&apos;s violate the ISP by creating a single interface that combines both user and admin services.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">export interface IUserAdminService {
  getUsers(): User[];
  getUserById(id: string): User | undefined;
  promoteToAdmin(id: string): void;
}
</code></pre><figcaption>IUserAdminService.ts</figcaption></figure><h4 id="why-this-violates-isp">Why This Violates ISP</h4><p>By combining user and admin services into one interface, we force all clients to implement methods they might not need. For example, a client that only needs to retrieve users would also have to implement <code>promoteToAdmin</code>, even if it&apos;s not relevant to their functionality.</p><p><strong>Solution:</strong> Break down the interface into smaller, more specific interfaces (like <code>IUserService</code> and <code>IAdminService</code>). Clients should only implement the interfaces that are relevant to their needs.</p><h2 id="7-dependency-inversion-principle-dip">7. Dependency Inversion Principle (DIP)</h2><h3 id="dip-in-action">DIP in Action</h3><p>The <strong>Dependency Inversion Principle</strong> suggests that high-level modules should not depend on low-level modules but on abstractions. Let&apos;s implement a simple user repository.</p><h4 id="step-1-define-the-repository-interface">Step 1: Define the Repository Interface</h4><figure class="kg-card kg-code-card"><pre><code class="language-javascript">// src/repositories/IUserRepository.ts
export interface IUserRepository {
  findAll(): User[];
  findById(id: string): User | undefined;
}
</code></pre><figcaption>IUserRepository.ts</figcaption></figure><h4 id="step-2-implement-the-repository">Step 2: Implement the Repository</h4><figure class="kg-card kg-code-card"><pre><code class="language-javascript">// src/repositories/UserRepository.ts
import { IUserRepository } from &apos;./IUserRepository&apos;;
import { User } from &apos;../entities/User&apos;;

export class UserRepository implements IUserRepository {
  private users: User[] = [
    { id: &apos;1&apos;, name: &apos;John Doe&apos;, email: &apos;john@example.com&apos; },
    { id: &apos;2&apos;, name: &apos;Jane Smith&apos;, email: &apos;jane@example.com&apos; },
  ];

  findAll(): User[] {
    return this.users;
  }

  findById(id: string): User | undefined {
    return this.users.find(user =&gt; user.id === id);
  }
}
</code></pre><figcaption>UserRepository.ts</figcaption></figure><h4 id="step-3-use-the-repository-in-the-resolver">Step 3: Use the Repository in the Resolver</h4><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import { UserRepository } from &apos;../repositories/UserRepository&apos;;

@Resolver(User)
export class UserResolver {
  private userRepository: IUserRepository;

  constructor() {
    this.userRepository = new UserRepository();
  }

  @Query(() =&gt; [User])
  getUsers(): User[] {
    return this.userRepository.findAll();
  }
}
</code></pre><figcaption>UserResolver.ts</figcaption></figure><h3 id="dip-violation-example">DIP Violation Example</h3><p>Let&apos;s violate DIP by directly depending on a low-level implementation instead of an abstraction.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import { UserRepository } from &apos;../repositories/UserRepository&apos;;

@Resolver(User)
export class UserResolver {
  private userRepository = new UserRepository(); // Direct dependency

  @Query(() =&gt; [User])
  getUsers(): User[] {
    return this.userRepository.findAll();
  }
}
</code></pre><figcaption>UserResolver</figcaption></figure><h4 id="why-this-violates-dip">Why This Violates DIP</h4><p>The <code>UserResolver</code> is tightly coupled to the <code>UserRepository</code> implementation. If we ever need to switch to a different repository implementation, we would need to modify the <code>UserResolver</code>, which violates the DIP.</p><p><strong>Solution:</strong> Depend on abstractions, not implementations. Use an interface (<code>IUserRepository</code>) and inject the implementation:</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">@Resolver(User)
export class UserResolver {
  constructor(private userRepository: IUserRepository) {}

  @Query(() =&gt; [User])
  getUsers(): User[] {
    return this.userRepository.findAll();
  }
}
</code></pre><figcaption>UserResolver</figcaption></figure><p>This allows you to swap out the repository implementation without modifying the resolver.</p><h2 id="8-conclusion">8. Conclusion</h2><p>Applying SOLID principles in your TypeScript and Node.js applications can significantly improve code quality, making it more maintainable, scalable, and robust. By understanding and adhering to these principles, you can avoid common pitfalls and create a cleaner, more modular architecture. However, it&apos;s also important to recognize when these principles are being violated and the potential consequences of such violations. Through careful design and thoughtful application of SOLID principles, you can build better software that stands the test of time.</p>]]></content:encoded></item><item><title><![CDATA[Ultimate Advanced Guide — Backend GraphQL Developer]]></title><description><![CDATA[<p><strong>Objective:</strong> An <strong>advanced, production-grade, full-stack backend guide</strong> for mastering GraphQL . It covers <em>architecture patterns, performance optimization, security, team conventions, scaling strategies, schema governance, multi-tenancy, federation, observability, testing, and more</em>. Includes examples in <strong>TypeScript</strong>, both with and without <strong>NestJS</strong>, and <strong>Apollo Server</strong> setups. </p><h2 id="table-of-contents">Table of Contents</h2><ol><li>GraphQL Deep Dive &#x2014; Concepts,</li></ol>]]></description><link>https://blog.foujeupavel.com/guide-complet-senior-lead-backend-graphql-developer/</link><guid isPermaLink="false">68f94f26df4dfb05d24bb994</guid><category><![CDATA[APIs]]></category><category><![CDATA[Backend]]></category><category><![CDATA[Best Practices]]></category><category><![CDATA[Graphql]]></category><category><![CDATA[Typescript]]></category><category><![CDATA[Usefull]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Sun, 04 Feb 2024 10:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1490698900541-76d9b74bdcac?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDZ8fGd1aWRlfGVufDB8fHx8MTc2MTE2OTI5MHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1490698900541-76d9b74bdcac?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDZ8fGd1aWRlfGVufDB8fHx8MTc2MTE2OTI5MHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Ultimate Advanced Guide &#x2014; Backend GraphQL Developer"><p><strong>Objective:</strong> An <strong>advanced, production-grade, full-stack backend guide</strong> for mastering GraphQL . It covers <em>architecture patterns, performance optimization, security, team conventions, scaling strategies, schema governance, multi-tenancy, federation, observability, testing, and more</em>. Includes examples in <strong>TypeScript</strong>, both with and without <strong>NestJS</strong>, and <strong>Apollo Server</strong> setups. </p><h2 id="table-of-contents">Table of Contents</h2><ol><li>GraphQL Deep Dive &#x2014; Concepts, History, and Evolution</li><li>Schema Design Principles and Anti-patterns</li><li>Clean Architecture and Domain-driven Design for GraphQL</li><li>Implementation Layers: Schema, Resolver, Service, Repository</li><li>Apollo Server Advanced Configuration (with Plugins, Middleware, and Caching)</li><li>NestJS GraphQL Deep Integration (Code-First + Federation)</li><li>Schema Federation and Microservice Design (Federated Architecture Blueprint (Multi-Service Apollo Gateway)) </li><li>Multi-Tenant Federation Strategies</li><li>Cross-Service Caching and DataLoader Integration</li><li>Performance and Scaling &#x2014; DataLoader, Batching, Persisted Queries, CDN, and Query Costing</li><li>Security Architecture (AuthN/Z, Complexity, Depth, Rate Limits, Abuse Protection)</li><li>API Governance, Versioning, and Schema Registry</li><li>Advanced Pagination, Filtering, Sorting, and Cursor Design</li><li>Subscriptions and Real-Time GraphQL (WebSockets, Kafka, Redis, RabbitMQ)</li><li>Testing Strategy (Unit, Integration, Contract, E2E, Snapshot Testing)</li><li>Observability and Monitoring (OpenTelemetry, Tracing, Metrics, Logs)</li><li>DevOps, CI/CD, and Deployment Strategies (Kubernetes, Serverless, CDN)</li><li>Developer Experience: Tooling, Codegen, Linting, Type Safety, Documentation</li><li>Error Handling, Logging, and Resilience Patterns</li><li>Performance Benchmarks, Profiling, and Query Optimization</li><li>Production Checklists and Best Practices</li><li>Common Interview Scenarios, System Design Questions, and Answers</li><li>Appendix: Tools, Libraries, and Resources</li></ol><h2 id="introduction-and-philosophy">Introduction and Philosophy</h2><p>GraphQL is not just a <em>replacement</em> for REST &#x2014; it&#x2019;s a <strong>declarative contract</strong> between clients and servers. As a GraphQL Developer, your responsibilities are to ensure:</p><ul><li><strong>Schema quality</strong> (easy to use, stable, evolvable)</li><li><strong>Separation of concerns</strong> (thin resolvers, rich services)</li><li><strong>Performance &amp; security</strong> (DataLoader, depth limits, complexity analysis)</li><li><strong>Observability</strong> (resolver-level tracing and metrics)</li><li><strong>Developer experience</strong> (documentation, codegen, secure playground)</li></ul><hr><h2 id="1-graphql-deep-dive-%E2%80%94-concepts-history-and-evolution">1. GraphQL Deep Dive &#x2014; Concepts, History, and Evolution</h2><p>GraphQL was introduced by Facebook (2012, public 2015) to replace inefficient REST endpoints with a single endpoint that allows <strong>declarative, shape-specific data fetching</strong>. Its declarative query model lets clients specify <em>what</em> they need, while the server defines <em>how</em> to fetch it.</p><h3 id="strengths">Strengths</h3><ul><li>Eliminates over-fetching and under-fetching.</li><li>Enables schema-driven contracts.</li><li>Integrates perfectly with type systems like TypeScript.</li><li>Enables schema introspection, developer tooling, and powerful self-documentation.</li></ul><h3 id="challenges">Challenges</h3><ul><li>Performance pitfalls (N+1 problem).</li><li>Complexity and depth abuse.</li><li>Evolving schemas safely.</li></ul><h3 id="key-features">Key Features</h3><ul><li><strong>Single endpoint, multiple entry points (Query/Mutation/Subscription)</strong></li><li><strong>Introspection</strong> for self-documentation</li><li><strong>Custom Scalars</strong> for advanced types (e.g., <code>DateTime</code>, <code>Email</code>, <code>Decimal</code>)</li><li><strong>Directives</strong> for metadata or logic control (e.g., <code>@auth</code>, <code>@deprecated</code>)</li></ul><hr><h2 id="2-schema-design-principles-and-anti-patterns">2. Schema Design Principles and Anti-patterns</h2><p>A schema is the <em>public contract</em> of your API. Treat it like an API product.</p><h3 id="principles">Principles</h3><h3 id="21-model-around-business-capabilities-not-database-tables">2.1 Model around business capabilities, not database tables.</h3><p><strong>Concept:</strong><br>Design your GraphQL schema based on <em>what the business does</em>, not <em>how the database stores data</em>.<br>GraphQL is a <em>domain API</em>, not a direct reflection of your persistence layer.</p><p><strong>Bad Example (DB-driven)</strong>:</p><pre><code class="language-javascript">type User {
  id: ID!
  name: String!
  email: String!
  roleId: ID!
}

type Role {
  id: ID!
  name: String!
}

type Query {
  users: [User!]!
  roles: [Role!]!
}
</code></pre><blockquote><em>&#x274C; The schema mirrors your tables directly.<br>There&#x2019;s no concept of what the user does or represents in the business.</em></blockquote><p><strong>Good Example (Business-driven)</strong>:</p><pre><code class="language-javascript">type User {
  id: ID!
  name: String!
  email: String!
  role: Role!
  permissions: [Permission!]!
  organization: Organization
}

type Organization {
  id: ID!
  name: String!
  members: [User!]!
  activeProjects: [Project!]!
}

type Query {
  getOrganizationOverview(id: ID!): OrganizationOverview!
}
</code></pre><p><strong>Resolver (NestJS example)</strong>:</p><pre><code class="language-javascript">@Resolver(() =&gt; Organization)
export class OrganizationResolver {
  constructor(private readonly orgService: OrganizationService) {}

  @Query(() =&gt; OrganizationOverview)
  async getOrganizationOverview(@Args(&apos;id&apos;) id: string) {
    return this.orgService.getOverview(id); // Fetches users, projects, KPIs
  }
}
</code></pre><blockquote><em>&#x2705; The schema now reflects <strong>business capabilities</strong> like &#x201C;organization overview,&#x201D; &#x201C;active projects,&#x201D; etc., not raw tables.</em></blockquote><p></p><h3 id="22-favor-composition-over-inheritance">2.2 Favor composition over inheritance</h3><p><strong>Concept:</strong><br>GraphQL&#x2019;s type system supports interfaces and unions, but overusing inheritance leads to rigid APIs.<br>Instead, <strong>compose</strong> small reusable types.</p><p><strong>Bad Example (Inheritance abuse):</strong></p><pre><code class="language-javascript">interface Content {
  id: ID!
  title: String!
}

type BlogPost implements Content {
  id: ID!
  title: String!
  body: String!
}

type VideoPost implements Content {
  id: ID!
  title: String!
  videoUrl: String!
}
</code></pre><blockquote>&#x274C; Tightly coupled &#x2014; every content type must implement <code>Content</code>.<br>Hard to evolve if a new type doesn&#x2019;t fit exactly.</blockquote><p><strong>Good Example (Composition):</strong></p><pre><code class="language-javascript">type Metadata {
  title: String!
  slug: String!
}

type BlogPost {
  id: ID!
  metadata: Metadata!
  body: String!
}

type VideoPost {
  id: ID!
  metadata: Metadata!
  videoUrl: String!
}
</code></pre><blockquote><em>&#x2705; Shared structure (metadata) via composition &#x2014; more flexible, easier to extend.</em></blockquote><h3 id="23-avoid-overloading-queries-%E2%80%94-separate-mutations-clearly">2.3 Avoid overloading queries &#x2014; separate mutations clearly.</h3><p><strong>Concept:</strong><br>Queries should <strong>read</strong>. Mutations should <strong>change state</strong>.<br>Avoid overloading queries to perform side effects (like logging, updates, etc.).</p><p><strong>Bad Example (Violates separation):</strong></p><pre><code class="language-javascript">type Query {
  getUserAndIncrementViews(userId: ID!): User!
}
</code></pre><p><strong>Good Example (Separation of concerns):</strong></p><pre><code class="language-javascript">type Query {
  getUser(userId: ID!): User!
}

type Mutation {
  incrementUserViews(userId: ID!): User!
}
</code></pre><p><strong>Resolvers:</strong></p><pre><code class="language-javascript">@Query(() =&gt; User)
getUser(@Args(&apos;userId&apos;) userId: string) {
  return this.userService.findById(userId);
}

@Mutation(() =&gt; User)
incrementUserViews(@Args(&apos;userId&apos;) userId: string) {
  return this.userService.incrementViews(userId);
}
</code></pre><h3 id="24-keep-inputs-granular-and-avoid-nested-mutations-complex-transaction-handling">2.4 Keep inputs granular, and avoid nested mutations (complex transaction handling)</h3><p><strong>Concept:</strong><br>Don&#x2019;t build giant nested input trees where one mutation creates a parent, several children, and grandchildren in one call.<br>It&#x2019;s brittle and hard to manage transactions across layers.</p><p><strong>Bad Example:</strong></p><pre><code class="language-javascript">mutation {
  createOrder(input: {
    customer: { name: &quot;John&quot; }
    products: [
      { id: &quot;p1&quot;, quantity: 2 },
      { id: &quot;p2&quot;, quantity: 1 }
    ]
  }) {
    id
  }
}</code></pre><blockquote>&#x274C; One mutation triggers multiple table writes (Customer, Order, OrderItems).<br>Hard to validate or rollback partially failed operations.</blockquote><p><strong>Good Example (Granular inputs):</strong></p><pre><code class="language-javascript">input CreateCustomerInput {
  name: String!
}

input AddProductToOrderInput {
  orderId: ID!
  productId: ID!
  quantity: Int!
}

type Mutation {
  createCustomer(input: CreateCustomerInput!): Customer!
  createOrder(customerId: ID!): Order!
  addProductToOrder(input: AddProductToOrderInput!): Order!
}
</code></pre><blockquote>&#x2705; Smaller mutations are easier to validate, test, and scale independently.<br>&#x2705; If you need a &#x201C;composite operation,&#x201D; handle it in your <strong>service layer</strong>, not the schema.</blockquote><p><strong>Service Layer Example (TypeScript):</strong></p><pre><code class="language-javascript">async createOrderWithProducts(customerId: string, items: ProductItemInput[]) {
  const order = await this.orderRepo.create({ customerId });
  for (const item of items) {
    await this.orderRepo.addProduct(order.id, item);
  }
  return order;
}
</code></pre><h3 id="25-anti-patterns">2.5 Anti-Patterns</h3><ul><li>&#x201C;God Query&#x201D;: One query returning everything.</li><li>&#x201C;Thin Schema, Fat Resolver&#x201D;: schema with poor type definitions, logic in resolvers.</li><li>Field explosion &#x2014; too many optional fields without grouping or pagination.</li></ul><h3 id="example-%E2%80%94-correct-design">Example &#x2014; Correct Design</h3><pre><code class="language-javascript">type Query {
me: User!
users(filter: UserFilter, pagination: PageInput): UserConnection!
post(id: ID!): Post
}


type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(input: UpdateUserInput!): User!
}


input CreateUserInput {
email: String!
name: String
}


input UserFilter {
search: String
isActive: Boolean
}</code></pre><h2 id="3-clean-architecture-and-ddd-for-graphql">3. Clean Architecture and DDD for GraphQL</h2><p>GraphQL is an interface layer &#x2014; your business logic should live <strong>below</strong> it.</p><h3 id="clean-layer-separation">Clean Layer Separation</h3><pre><code class="language-typescript">src/
&#x251C;&#x2500;&#x2500; domain/ # Entities, Value Objects, Repositories
&#x251C;&#x2500;&#x2500; usecases/ # Business rules
&#x251C;&#x2500;&#x2500; infra/ # DB, external services
&#x251C;&#x2500;&#x2500; graphql/ # Schema + Resolvers (presentation)
&#x2514;&#x2500;&#x2500; app.ts # Server bootstrap</code></pre><h3 id="benefits">Benefits</h3><ul><li>Framework-agnostic</li><li>Testable usecases</li><li>No direct dependency between GraphQL resolvers and persistence</li></ul><h3 id="example">Example</h3><pre><code class="language-javascript">// domain/user.entity.ts
export class User {
    constructor(public id: string, public email: string, public name ? : string) {}
}


// usecases/create-user.usecase.ts

export class CreateUserUsecase {
    constructor(private userRepo: UserRepo) {}
    async execute(input: CreateUserInput): Promise &lt; User &gt; {
        const existing = await this.userRepo.findByEmail(input.email);
        if (existing) throw new Error(&apos;Email exists&apos;);
        return this.userRepo.create(new User(uuid(), input.email, input.name));
    }
}</code></pre><h2 id="4-implementation-layers">4. Implementation Layers</h2><p>a<strong>) Concept Overview</strong></p><p>GraphQL apps are often built too tightly &#x2014; schema definitions and database queries end up mixed in resolvers.<br>At scale, this leads to technical debt and coupling.</p><p>Instead, you should follow a <strong>layered architecture</strong>:</p><!--kg-card-begin: html--><table data-start="772" data-end="1200" class="w-fit min-w-(--thread-content-width)"><thead data-start="772" data-end="798"><tr data-start="772" data-end="798"><th data-start="772" data-end="780" data-col-size="sm">Layer</th><th data-start="780" data-end="798" data-col-size="md">Responsibility</th></tr></thead><tbody data-start="827" data-end="1200"><tr data-start="827" data-end="928"><td data-start="827" data-end="854" data-col-size="sm"><strong data-start="829" data-end="853">Schema (GraphQL SDL)</strong></td><td data-col-size="md" data-start="854" data-end="928">Defines the <em data-start="868" data-end="885">public contract</em> of your API &#x2014; types, queries, mutations.</td></tr><tr data-start="929" data-end="1009"><td data-start="929" data-end="944" data-col-size="sm"><strong data-start="931" data-end="943">Resolver</strong></td><td data-col-size="md" data-start="944" data-end="1009">Maps schema operations to business logic. Should remain thin.</td></tr><tr data-start="1010" data-end="1106"><td data-start="1010" data-end="1024" data-col-size="sm"><strong data-start="1012" data-end="1023">Service</strong></td><td data-col-size="md" data-start="1024" data-end="1106">Contains business logic &#x2014; orchestrates multiple repositories or external APIs.</td></tr><tr data-start="1107" data-end="1200"><td data-start="1107" data-end="1124" data-col-size="sm"><strong data-start="1109" data-end="1123">Repository</strong></td><td data-col-size="md" data-start="1124" data-end="1200">Handles data persistence (ORM/DB queries). Should be framework-agnostic.</td></tr></tbody></table><!--kg-card-end: html--><p>This ensures:</p><ul><li>Each layer is independently testable.</li><li>The domain logic stays clean, not tied to GraphQL.</li><li>You can swap the ORM or even the transport (REST, gRPC) easily.</li></ul><hr><h2 id="b-example-schema-%E2%86%92-resolver-%E2%86%92-service-%E2%86%92-repository">b) Example (Schema &#x2192; Resolver &#x2192; Service &#x2192; Repository)</h2><p>We demonstrating this pattern with:</p><ul><li>GraphQL schema using decorators (NestJS)</li><li>Clean separation between layers</li><li>Prisma-based repository</li><li>Validation and error handling</li></ul><h3 id="example-%E2%80%94-apollo-prisma">Example &#x2014; Apollo + Prisma</h3><pre><code class="language-javascript">// src/users/users.module.ts
import { Module, Injectable } from &apos;@nestjs/common&apos;;
import { Resolver, Query, Mutation, Args, ObjectType, Field, InputType, ID } from &apos;@nestjs/graphql&apos;;
import { PrismaClient, User as PrismaUser } from &apos;@prisma/client&apos;;
import { GraphQLError } from &apos;graphql&apos;;

// =======================
// SCHEMA (GraphQL Types)
// =======================
@ObjectType()
class User {
  @Field(() =&gt; ID)
  id: string;

  @Field()
  email: string;

  @Field({ nullable: true })
  name?: string;

  @Field()
  createdAt: Date;
}

@InputType()
class CreateUserInput {
  @Field()
  email: string;

  @Field({ nullable: true })
  name?: string;
}

@InputType()
class UpdateUserInput {
  @Field(() =&gt; ID)
  id: string;

  @Field({ nullable: true })
  name?: string;
}

// =======================
// REPOSITORY LAYER
// =======================
@Injectable()
class UserRepository {
  private prisma = new PrismaClient();

  async findAll(): Promise&lt;PrismaUser[]&gt; {
    return this.prisma.user.findMany();
  }

  async findById(id: string): Promise&lt;PrismaUser | null&gt; {
    return this.prisma.user.findUnique({ where: { id } });
  }

  async create(data: { email: string; name?: string }): Promise&lt;PrismaUser&gt; {
    return this.prisma.user.create({ data });
  }

  async update(id: string, data: { name?: string }): Promise&lt;PrismaUser&gt; {
    return this.prisma.user.update({ where: { id }, data });
  }

  async delete(id: string): Promise&lt;PrismaUser&gt; {
    return this.prisma.user.delete({ where: { id } });
  }
}

// =======================
// SERVICE LAYER
// =======================
@Injectable()
class UserService {
  constructor(private readonly repo: UserRepository) {}

  async listUsers() {
    return this.repo.findAll();
  }

  async getUser(id: string) {
    const user = await this.repo.findById(id);
    if (!user) throw new GraphQLError(`User ${id} not found`);
    return user;
  }

  async createUser(input: CreateUserInput) {
    const existing = await this.repo.findAll();
    if (existing.some((u) =&gt; u.email === input.email)) {
      throw new GraphQLError(&apos;Email already in use&apos;);
    }
    return this.repo.create(input);
  }

  async updateUser(input: UpdateUserInput) {
    await this.getUser(input.id); // validate existence
    return this.repo.update(input.id, { name: input.name });
  }

  async deleteUser(id: string) {
    await this.getUser(id);
    return this.repo.delete(id);
  }
}

// =======================
// RESOLVER LAYER
// =======================
@Resolver(() =&gt; User)
class UserResolver {
  constructor(private readonly service: UserService) {}

  @Query(() =&gt; [User])
  async users() {
    return this.service.listUsers();
  }

  @Query(() =&gt; User)
  async user(@Args(&apos;id&apos;, { type: () =&gt; String }) id: string) {
    return this.service.getUser(id);
  }

  @Mutation(() =&gt; User)
  async createUser(@Args(&apos;data&apos;) data: CreateUserInput) {
    return this.service.createUser(data);
  }

  @Mutation(() =&gt; User)
  async updateUser(@Args(&apos;data&apos;) data: UpdateUserInput) {
    return this.service.updateUser(data);
  }

  @Mutation(() =&gt; User)
  async deleteUser(@Args(&apos;id&apos;) id: string) {
    return this.service.deleteUser(id);
  }
}

// =======================
// MODULE DEFINITION
// =======================
@Module({
  providers: [UserResolver, UserService, UserRepository],
})
export class UsersModule {}
</code></pre><p><strong>c) Explanation</strong></p><!--kg-card-begin: html--><table data-start="5053" data-end="5623" class="w-fit min-w-(--thread-content-width)"><thead data-start="5053" data-end="5089"><tr data-start="5053" data-end="5089"><th data-start="5053" data-end="5061" data-col-size="sm">Layer</th><th data-start="5061" data-end="5078" data-col-size="md">Responsibility</th><th data-start="5078" data-end="5089" data-col-size="md">Example</th></tr></thead><tbody data-start="5129" data-end="5623"><tr data-start="5129" data-end="5281"><td data-start="5129" data-end="5152" data-col-size="sm"><strong data-start="5131" data-end="5151">Schema (GraphQL)</strong></td><td data-start="5152" data-end="5227" data-col-size="md">Declares what data the API exposes and how clients can interact with it.</td><td data-start="5227" data-end="5281" data-col-size="md"><code data-start="5229" data-end="5242">@ObjectType</code>, <code data-start="5244" data-end="5256">@InputType</code>, <code data-start="5258" data-end="5266">@Query</code>, <code data-start="5268" data-end="5279">@Mutation</code></td></tr><tr data-start="5282" data-end="5392"><td data-start="5282" data-end="5297" data-col-size="sm"><strong data-start="5284" data-end="5296">Resolver</strong></td><td data-start="5297" data-end="5356" data-col-size="md">Translates GraphQL operations into business logic calls.</td><td data-start="5356" data-end="5392" data-col-size="md">Thin layer: delegates to Service</td></tr><tr data-start="5393" data-end="5498"><td data-start="5393" data-end="5407" data-col-size="sm"><strong data-start="5395" data-end="5406">Service</strong></td><td data-start="5407" data-end="5456" data-col-size="md">Holds domain logic, validation, orchestration.</td><td data-start="5456" data-end="5498" data-col-size="md">Ensures clean, reusable business rules</td></tr><tr data-start="5499" data-end="5623"><td data-start="5499" data-end="5516" data-col-size="sm"><strong data-start="5501" data-end="5515">Repository</strong></td><td data-start="5516" data-end="5595" data-col-size="md">Directly interacts with the database via Prisma (or TypeORM, MikroORM, etc.)</td><td data-start="5595" data-end="5623" data-col-size="md">Handles persistence only</td></tr></tbody></table><!--kg-card-end: html--><hr><h2 id="%E2%9C%85-benefits">&#x2705; Benefits</h2><ul><li> <strong>Single Responsibility</strong> &#x2014; each layer does one thing well.</li><li><strong>Reusability</strong> &#x2014; service logic can power REST, gRPC, or jobs.</li><li><strong>Testability</strong> &#x2014; easily mock repositories and test services independently.</li><li><strong>Maintainability</strong> &#x2014; large codebases scale better with this separation.</li><li>&#x1F510; <strong>Security &amp; Validation</strong> &#x2014; centralized domain checks in services.</li></ul><h3 id="5-apollo-server-advanced-configuration">5. Apollo Server Advanced Configuration</h3><p>Before diving into configuration examples, it&#x2019;s important to understand that <strong>Apollo Server</strong> isn&#x2019;t just a schema + resolver runner &#x2014; it&#x2019;s a <strong>gateway for observability, security, performance, and developer experience</strong>.</p><p>When working at <strong>scale or in production environments</strong>, advanced configuration becomes critical to handle:</p><ul><li><strong>Performance optimization</strong> &#x2014; controlling query depth, cost, batching, and caching.</li><li><strong>Security &amp; validation</strong> &#x2014; applying query whitelisting, complexity analysis, and request limits.</li><li><strong>Telemetry &amp; monitoring</strong> &#x2014; integrating OpenTelemetry, Prometheus, or Apollo Studio metrics.</li><li><strong>Schema modularization</strong> &#x2014; loading schemas dynamically from multiple services or modules.</li><li><strong>Context enrichment</strong> &#x2014; injecting user sessions, auth tokens, or request-scoped data.</li><li><strong>Plugin ecosystem</strong> &#x2014; adding logging, tracing, or auditing middleware hooks.</li><li><strong>Server lifecycle management</strong> &#x2014; customizing startup, shutdown, and error handling flows.</li></ul><p>In a real-world GraphQL backend, your Apollo Server is the <strong>core execution environment</strong>, and configuring it properly can dramatically improve <strong>stability</strong>, <strong>observability</strong>, and <strong>developer velocity</strong>.</p><h3 id="example-setup-overview">Example Setup Overview</h3><p>An advanced Apollo Server setup usually includes:</p><ul><li>A <strong>schema loader</strong> (<code>@graphql-tools/load</code> or module federation)</li><li><strong>Performance controls</strong>: query depth, cost analysis, caching policies.</li><li><strong>Custom context factory</strong>: to inject per-request user/session data.</li><li><strong>Plugins</strong>: logging, metrics, tracing, and auditing.</li><li><strong>Health checks</strong> and <strong>graceful shutdown</strong> hooks.</li><li><strong>Integration</strong> with external systems (Redis cache, DataLoader batching, etc.).</li></ul><p><strong>Exemple:</strong></p><pre><code class="language-javascript">import { ApolloServer } from &apos;@apollo/server&apos;;
import { ApolloServerPluginDrainHttpServer } from &apos;@apollo/server/plugin/drainHttpServer&apos;;
import depthLimit from &apos;graphql-depth-limit&apos;;
import { makeExecutableSchema } from &apos;@graphql-tools/schema&apos;;
import { typeDefs, resolvers } from &apos;./schema&apos;;
import http from &apos;http&apos;;
import { contextFactory } from &apos;./context&apos;;
import { loggingPlugin, tracingPlugin } from &apos;./plugins&apos;;
import { rateLimitPlugin } from &apos;./security/rateLimit&apos;;

const schema = makeExecutableSchema({ typeDefs, resolvers });
const httpServer = http.createServer();

const server = new ApolloServer({
  schema,
  validationRules: [depthLimit(10)], // prevent deep queries
  context: contextFactory, // inject user/session info
  plugins: [
    ApolloServerPluginDrainHttpServer({ httpServer }),
    loggingPlugin(),
    tracingPlugin(),
    rateLimitPlugin({ max: 100, window: &apos;1m&apos; }),
  ],
  introspection: process.env.NODE_ENV !== &apos;production&apos;,
  formatError: (error) =&gt; {
    console.error(&apos;GraphQL Error:&apos;, error);
    return { message: error.message, code: error.extensions?.code };
  },
});

await server.start();
httpServer.listen({ port: 4000 });
</code></pre><h3 id="6-nestjs-graphql-deep-integration">6. NestJS GraphQL Deep Integration</h3><p>NestJS is more than a framework: it&#x2019;s an opinionated architecture that brings <strong>Dependency Injection (DI)</strong>, <strong>modularity</strong>, and <strong>decorator-driven</strong> APIs to Node.js. When you combine NestJS with GraphQL you get a powerful platform for building <strong>large, maintainable</strong>, and <strong>testable</strong> GraphQL backends.</p><p>As a GraphQL developer you should focus on:</p><ul><li><strong>Code-First vs Schema-First</strong> &#x2014; Code-First (decorators + classes) gives excellent TypeScript safety and auto-schema generation; Schema-First (SDL) gives designers and non-TS teams control. Pick based on team needs.</li><li><strong>Scoped providers &amp; per-request state</strong> &#x2014; Create request-scoped providers for DataLoader, auth context, and transaction management to avoid cross-request leakage.</li><li><strong>Field-level guards &amp; interceptors</strong> &#x2014; Use Nest guards for auth/authorization and interceptors for logging, tracing and response shaping.</li><li><strong>Federation support</strong> &#x2014; Build subgraphs with <code>@nestjs/graphql</code> federation features (<code>@Key</code>, <code>@ResolveReference</code>) for microservice ownership.</li><li><strong>Performance &amp; caching</strong> &#x2014; Leverage DataLoader, Redis caching, and field projection to minimize DB load.</li><li><strong>Testing &amp; lifecycle hooks</strong> &#x2014; Unit-test resolvers with DI mocks, integration-test modules with in-memory DB or testcontainers; use lifecycle hooks for startup/shutdown tasks.</li><li><strong>Observability</strong> &#x2014; Integrate OpenTelemetry instrumentation at resolver/service level and export traces/metrics.</li></ul><h3 id="example-%E2%80%94-nestjs-code-first-graphql-typescript">Example &#x2014; NestJS Code-First GraphQL (TypeScript)</h3><p>This example shows:</p><ul><li><code>UsersModule</code> with <code>UsersService</code> and <code>UsersResolver</code></li><li>Request-scoped <code>LoadersService</code> providing DataLoader instances</li><li>A guard for auth</li><li>Federation-ready entity with <code>ResolveReference</code></li><li>Context injection in <code>GraphQLModule.forRoot</code></li></ul><pre><code class="language-javascript">// main.ts
import { NestFactory } from &apos;@nestjs/core&apos;;
import { AppModule } from &apos;./app.module&apos;;
import { ValidationPipe } from &apos;@nestjs/common&apos;;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
  await app.listen(4000);
}
bootstrap();
</code></pre><pre><code class="language-javascript">// app.module.ts
import { Module } from &apos;@nestjs/common&apos;;
import { GraphQLModule } from &apos;@nestjs/graphql&apos;;
import { join } from &apos;path&apos;;
import { UsersModule } from &apos;./users/users.module&apos;;

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: join(process.cwd(), &apos;src/schema.gql&apos;), // Code-First
      context: ({ req }) =&gt; ({ req }), 
      buildSchemaOptions: { fieldResolverEnhancers: [&apos;guards&apos;, &apos;interceptors&apos;] },
      playground: process.env.NODE_ENV !== &apos;production&apos;,
      introspection: process.env.NODE_ENV !== &apos;production&apos;,
    }),
    UsersModule,
  ],
})
export class AppModule {}
</code></pre><pre><code class="language-javascript">// users/user.entity.ts
import { ObjectType, Field, ID } from &apos;@nestjs/graphql&apos;;
import { Key } from &apos;@nestjs/graphql/dist/federation&apos;; // federation decorators

@ObjectType()
@Key(&apos;id&apos;) // federation key
export class UserEntity {
  @Field(() =&gt; ID) id: string;
  @Field() email: string;
  @Field({ nullable: true }) name?: string;
}
</code></pre><pre><code class="language-javascript">// users/loaders.service.ts
import { Injectable, Scope } from &apos;@nestjs/common&apos;;
import DataLoader from &apos;dataloader&apos;;
import { UsersService } from &apos;./users.service&apos;;

// Request-scoped provider so loaders are per-request and safe to cache
@Injectable({ scope: Scope.REQUEST })
export class LoadersService {
  public userLoader: DataLoader&lt;string, any&gt;;

  constructor(private usersService: UsersService) {
    this.userLoader = new DataLoader(async (ids: readonly string[]) =&gt; {
      const users = await this.usersService.findByIds(ids as string[]);
      const map = new Map(users.map(u =&gt; [u.id, u]));
      return ids.map(id =&gt; map.get(id) ?? null);
    });
  }
}
</code></pre><pre><code class="language-javascript">// users/users.service.ts
import { Injectable } from &apos;@nestjs/common&apos;;
// imagine prisma injected or repo pattern
@Injectable()
export class UsersService {
  constructor(private readonly repo /* e.g. PrismaService */) {}

  async findByIds(ids: string[]) {
    // use repo with `where id in (...)` and projection based on need
    return this.repo.user.findMany({ where: { id: { in: ids } }});
  }

  async findOneById(id: string) {
    return this.repo.user.findUnique({ where: { id }});
  }
}
</code></pre><pre><code class="language-javascript">// users/users.resolver.ts
import { Resolver, Query, Args, ResolveReference, ResolveField, Parent, Context } from &apos;@nestjs/graphql&apos;;
import { UseGuards } from &apos;@nestjs/common&apos;;
import { GqlAuthGuard } from &apos;../auth/gql-auth.guard&apos;;
import { UsersService } from &apos;./users.service&apos;;
import { LoadersService } from &apos;./loaders.service&apos;;
import { UserEntity } from &apos;./user.entity&apos;;

@Resolver(() =&gt; UserEntity)
export class UsersResolver {
  constructor(private usersService: UsersService, private loaders: LoadersService) {}

  @Query(() =&gt; UserEntity)
  @UseGuards(GqlAuthGuard)
  async user(@Args(&apos;id&apos;) id: string) {
    return this.usersService.findOneById(id);
  }

  // Field resolver using the request-scoped DataLoader
  @ResolveField(&apos;friends&apos;, () =&gt; [UserEntity])
  async friends(@Parent() user: any) {
    return this.loaders.userLoader.loadMany(user.friendIds || []);
  }

  // Federation: resolve reference when gateway asks
  @ResolveReference()
  resolveReference(reference: { __typename: string; id: string }) {
    return this.usersService.findOneById(reference.id);
  }
}
</code></pre><pre><code class="language-javascript">// users/users.module.ts
import { Module } from &apos;@nestjs/common&apos;;
import { UsersResolver } from &apos;./users.resolver&apos;;
import { UsersService } from &apos;./users.service&apos;;
import { LoadersService } from &apos;./loaders.service&apos;;

@Module({
  providers: [UsersResolver, UsersService, LoadersService],
  exports: [UsersService],
})
export class UsersModule {}
</code></pre><p><strong>Notes &amp; Best Practices for this pattern</strong></p><ul><li><strong>Scoped Loaders:</strong> mark loader providers as <code>Scope.REQUEST</code> so each HTTP request gets its own loaders (no cross-request caching).</li><li><strong>Guards &amp; Roles:</strong> implement <code>GqlAuthGuard</code> to extract JWT from headers (or cookies) and attach <code>user</code> to <code>context</code>. Use role guards on resolvers or fields.</li><li><strong>Field Resolver Enhancers:</strong> <code>buildSchemaOptions.fieldResolverEnhancers</code> enables using Nest guards/interceptors on field resolvers.</li><li><strong>Projections:</strong> when possible, use <code>info</code> or custom decorators to project DB fields to minimize selects (Prisma <code>select</code>).</li><li><strong>Transactions:</strong> orchestrate complex multi-entity writes in service/usecase layer and manage DB transactions there (not in resolvers).</li><li><strong>Testing:</strong> use Nest&#x2019;s <code>Test.createTestingModule</code> and mock injected providers (e.g., a mocked <code>UsersService</code>) to unit-test resolvers.</li><li><strong>Federation:</strong> to make the module a federated subgraph, use <code>@Key</code>, <code>@ResolveReference</code>, and the <code>@nestjs/graphql</code> federation utilities.</li></ul><h3 id="federation-support">Federation Support</h3><pre><code class="language-javascript">GraphQLFederationModule.forRoot({
    autoSchemaFile: true,
    gateway: {
        serviceList: [{
            name: &apos;users&apos;,
            url: &apos;http://users:4001/graphql&apos;
        }]
    }
});</code></pre><h2 id="7-schema-federation-and-microservice-design">7. Schema Federation and Microservice Design</h2><p>Apollo Federation enables multiple <strong>microservices</strong> (subgraphs) to provide <strong>portions of a unified GraphQL schema</strong>, combined via an <strong>Apollo Gateway</strong>. This allows:</p><ul><li>Independent deployments per service</li><li>Service-level ownership of entities</li><li>Schema evolution without breaking other teams</li><li>Efficient modular development</li><li>Each service defines a subgraph.</li><li>Apollo Gateway composes schemas.</li><li>Use entity references with <code>@key</code> directive.</li></ul><h3 id="components">Components</h3><ol><li><strong>Gateway</strong>: Central schema composition, query planning, and routing</li><li><strong>Subgraph Services</strong>: Each service exposes its portion of the schema</li><li><strong>Shared Entities</strong>: Entities are resolved across services using <code>@key</code> and <code>@requires</code></li><li><strong>Data Sources</strong>: Each service may use its own database, cache, or API</li></ol><h3 id="example-setup">Example Setup</h3><pre><code class="language-javascript">graphqld-federation-project/
&#x251C;&#x2500;&#x2500; gateway/
&#x2502; &#x2514;&#x2500;&#x2500; index.ts
&#x251C;&#x2500;&#x2500; services/
&#x2502; &#x251C;&#x2500;&#x2500; users/
&#x2502; &#x2502; &#x251C;&#x2500;&#x2500; src/
&#x2502; &#x2502; &#x2502; &#x251C;&#x2500;&#x2500; user.entity.ts
&#x2502; &#x2502; &#x2502; &#x251C;&#x2500;&#x2500; users.resolver.ts
&#x2502; &#x2502; &#x2502; &#x2514;&#x2500;&#x2500; users.module.ts
&#x2502; &#x251C;&#x2500;&#x2500; posts/
&#x2502; &#x2502; &#x251C;&#x2500;&#x2500; src/
&#x2502; &#x2502; &#x2502; &#x251C;&#x2500;&#x2500; post.entity.ts
&#x2502; &#x2502; &#x2502; &#x251C;&#x2500;&#x2500; posts.resolver.ts
&#x2502; &#x2502; &#x2502; &#x2514;&#x2500;&#x2500; posts.module.ts
&#x2502; &#x2514;&#x2500;&#x2500; comments/
&#x2502; &#x251C;&#x2500;&#x2500; src/
&#x2502; &#x2502; &#x251C;&#x2500;&#x2500; comment.entity.ts
&#x2502; &#x2502; &#x251C;&#x2500;&#x2500; comments.resolver.ts
&#x2502; &#x2502; &#x2514;&#x2500;&#x2500; comments.module.ts</code></pre><h3 id="user-service-subgraph">User Service (Subgraph)</h3><pre><code class="language-javascript">// users/user.entity.ts
import {
    ObjectType,
    Field,
    ID
} from &apos;@nestjs/graphql&apos;;
import {
    Directive,
    Key
} from &apos;@apollo/federation&apos;;


@ObjectType()
@Key(fields: &apos;id&apos;)
export class UserEntity {
    @Field(() =&gt; ID)
    id: string;


    @Field()
    email: string;
    @Field({
        nullable: true
    })
    name ? : string;
}


// users/users.resolver.ts
@Resolver(() =&gt; UserEntity)
export class UsersResolver {
    constructor(private usersService: UsersService) {}


    @Query(() =&gt; UserEntity)
    user(@Args(&apos;id&apos;) id: string) {
        return this.usersService.findOneById(id);
    }


    @ResolveReference()
    resolveReference(reference: {
        __typename: string;id: string
    }) {
        return this.usersService.findOneById(reference.id);
    }
}</code></pre><h3 id="posts-service-subgraph">Posts Service (Subgraph)</h3><pre><code class="language-javascript">@ObjectType()
@Key(fields: &apos;id&apos;)
export class PostEntity {
    @Field(() =&gt; ID)
    id: string;


    @Field()
    title: string;


    @Field(() =&gt; UserEntity)
    @Directive(&apos;@provides(fields: &quot;email&quot;)&apos;)
    author: UserEntity;
}</code></pre><h3 id="apollo-gateway-central-schema-composition">Apollo Gateway (Central Schema Composition)</h3><pre><code class="language-javascript">import {
    ApolloGateway
} from &apos;@apollo/gateway&apos;;
import {
    ApolloServer
} from &apos;apollo-server&apos;;


const gateway = new ApolloGateway({
    serviceList: [{
            name: &apos;users&apos;,
            url: &apos;http://localhost:4001/graphql&apos;
        },
        {
            name: &apos;posts&apos;,
            url: &apos;http://localhost:4002/graphql&apos;
        },
        {
            name: &apos;comments&apos;,
            url: &apos;http://localhost:4003/graphql&apos;
        },
    ],
});


const server = new ApolloServer({
    gateway,
    subscriptions: false,
    context: ({
        req
    }) =&gt; ({
        user: authenticate(req)
    }),
});


server.listen({
    port: 4000
}).then(({
    url
}) =&gt; console.log(`Gateway running at ${url}`));</code></pre><h3 id="key-federation-patterns">Key Federation Patterns</h3><ul><li><strong>@key</strong>: marks unique identifier for entity across services</li><li><strong>@extends</strong>: extend entity from another service</li><li><strong>@provides</strong>: service can provide additional fields to other services</li><li><strong>@requires</strong>: request fields from another service to resolve entity</li></ul><h3 id="deployment-considerations">Deployment Considerations</h3><ul><li>Each subgraph deploys independently; updates do not break gateway if backwards-compatible</li><li>Use service discovery (DNS, Consul, or Kubernetes services)</li><li>Horizontal scaling at service-level</li><li>Cache entity resolutions for high-demand queries</li><li>Monitor per-service metrics (latency, error rate, throughput)</li></ul><h3 id="advanced-features">Advanced Features</h3><ul><li><strong>Partial Schema Ownership:</strong> each team owns one subgraph</li><li><strong>Versioning per Subgraph:</strong> independent release cycles</li><li><strong>Federated Tracing:</strong> trace resolution across services</li><li><strong>Security per Subgraph:</strong> JWT validation, field-level authorization</li><li><strong>Subscription Federation:</strong> use shared pubsub (Redis/Kafka) for real-time updates</li></ul><p>This allow you to build <strong>microservice-based GraphQL backends</strong> with Apollo Federation, ensuring modularity, scalability, and maintainability.</p><h2 id="8-multi-tenant-federation-strategies">8. Multi-Tenant Federation Strategies</h2><h3 id="overview">Overview</h3><p>n a <strong>multi-tenant GraphQL federation</strong>, each client (tenant) interacts with the same GraphQL API surface &#x2014; but <strong>data, permissions, and analytics</strong> must remain isolated.<br>The challenge is to balance <strong>schema consistency</strong> with <strong>tenant-level isolation</strong> and <strong>performance efficiency</strong>.</p><!--kg-card-begin: html--><table data-start="812" data-end="1704" class="w-fit min-w-(--thread-content-width)"><thead data-start="812" data-end="857"><tr data-start="812" data-end="857"><th data-start="812" data-end="823" data-col-size="sm">Strategy</th><th data-start="823" data-end="837" data-col-size="lg">Description</th><th data-start="837" data-end="857" data-col-size="md">Typical Use Case</th></tr></thead><tbody data-start="906" data-end="1704"><tr data-start="906" data-end="1118"><td data-start="906" data-end="944" data-col-size="sm"><strong data-start="908" data-end="943">Context-Based Tenant Resolution</strong></td><td data-col-size="lg" data-start="944" data-end="1051">Tenant is derived from the auth token or headers (<code data-start="996" data-end="1009">x-tenant-id</code>) and passed through the GraphQL context.</td><td data-col-size="md" data-start="1051" data-end="1118">SaaS apps where all tenants share a single codebase and schema.</td></tr><tr data-start="1119" data-end="1316"><td data-start="1119" data-end="1145" data-col-size="sm"><strong data-start="1121" data-end="1144">Schema Partitioning</strong></td><td data-col-size="lg" data-start="1145" data-end="1240">Each tenant has its own GraphQL schema instance, sometimes generated dynamically at runtime.</td><td data-col-size="md" data-start="1240" data-end="1316">Enterprise clients needing custom schema extensions or data segregation.</td></tr><tr data-start="1317" data-end="1512"><td data-start="1317" data-end="1340" data-col-size="sm"><strong data-start="1319" data-end="1339">Database Scoping</strong></td><td data-col-size="lg" data-start="1340" data-end="1467">Tenants share the schema, but data is isolated in DB via <code data-start="1399" data-end="1410">tenant_id</code> column (single-DB) or schema-per-tenant (multi-schema).</td><td data-col-size="md" data-start="1467" data-end="1512">Most SaaS setups (e.g., HubSpot, Notion).</td></tr><tr data-start="1513" data-end="1704"><td data-start="1513" data-end="1536" data-col-size="sm"><strong data-start="1515" data-end="1535">Field-Level RBAC</strong></td><td data-col-size="lg" data-start="1536" data-end="1642">Enforces access restrictions via directives, middleware, or GraphQL Shield rules based on tenant roles.</td><td data-col-size="md" data-start="1642" data-end="1704">Fine-grained access control for multi-organization setups.</td></tr></tbody></table><!--kg-card-end: html--><p>Let&#x2019;s go through <strong>each strategy with example code</strong> and how it fits into a <strong>federated architecture</strong>.</p><ol><li><strong>Context-Based Tenant Resolution</strong>: Inject tenant info into context per request.</li></ol><p>Every incoming request includes a tenant identifier (JWT claims, API key, or <code>x-tenant-id</code> header).<br>This ID is injected into the <strong>GraphQL context</strong> and propagated across all subgraphs through the <strong>Apollo Gateway</strong>.</p><p><strong>Gateway Example</strong></p><pre><code class="language-javascript">// gateway/index.ts
import { ApolloGateway, IntrospectAndCompose } from &apos;@apollo/gateway&apos;;
import { ApolloServer } from &apos;@apollo/server&apos;;
import { startStandaloneServer } from &apos;@apollo/server/standalone&apos;;

const gateway = new ApolloGateway({
  supergraphSdl: new IntrospectAndCompose({
    subgraphs: [
      { name: &apos;users&apos;, url: &apos;http://localhost:4001/graphql&apos; },
      { name: &apos;billing&apos;, url: &apos;http://localhost:4002/graphql&apos; },
    ],
  }),
});

const server = new ApolloServer({
  gateway,
  context: async ({ req }) =&gt; {
    const tenantId = req.headers[&apos;x-tenant-id&apos;];
    if (!tenantId) throw new Error(&apos;Tenant header missing&apos;);
    return { tenantId };
  },
});

await startStandaloneServer(server, { listen: { port: 4000 } });
</code></pre><p><strong>Subgraph Example (NestJS)</strong></p><pre><code class="language-javascript">// app.module.ts
GraphQLModule.forRoot({
  autoSchemaFile: true,
  federationMetadata: true,
  context: ({ req }) =&gt; ({ tenantId: req.headers[&apos;x-tenant-id&apos;] }),
});
</code></pre><pre><code class="language-javascript">// users.service.ts
@Injectable()
export class UsersService {
  constructor(private readonly prisma: PrismaService) {}

  async findAll(tenantId: string) {
    return this.prisma.user.findMany({ where: { tenantId } });
  }
}</code></pre><pre><code class="language-javascript">// users.resolver.ts
@Resolver(() =&gt; UserEntity)
export class UsersResolver {
  constructor(private readonly service: UsersService) {}

  @Query(() =&gt; [UserEntity])
  async users(@Context(&apos;tenantId&apos;) tenantId: string) {
    return this.service.findAll(tenantId);
  }
}
</code></pre><p><strong>2. Schema Partitioning</strong>: </p><p>For <strong>strict tenant isolation</strong>, each tenant gets a separate schema instance &#x2014; possibly deployed as an <strong>isolated subgraph</strong>.<br>Useful when tenants have <strong>custom fields</strong>, <strong>features</strong>, or <strong>compliance requirements</strong> (e.g., GDPR, HIPAA).</p><pre><code class="language-javascript">// schema-loader.ts
import { buildSchema } from &apos;graphql&apos;;
import { TenantConfigService } from &apos;./tenant-config.service&apos;;

export async function buildTenantSchema(tenantId: string) {
  const config = await TenantConfigService.getConfig(tenantId);
  const typeDefs = `
    type Query {
      hello: String
      ${config.customFields.join(&apos;\n&apos;)}
    }
  `;
  return buildSchema(typeDefs);
}
</code></pre><p></p><p><strong>3. Database Scoping</strong>: Single DB with tenant_id column or schema-per-tenant pattern. Data isolation is enforced at the persistence layer, not the schema.<br>Two common patterns:</p><p>	a) <strong>Single Database, tenant_id column: </strong>All tenants share tables; every table includes <code>tenant_id</code>.</p><pre><code class="language-javascript">CREATE TABLE users (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  name TEXT,
  email TEXT
);
</code></pre><p>Then every query filters by <code>tenant_id</code>:</p><pre><code class="language-javascript">// user.service.ts
async findAll(tenantId: string) {
  return this.prisma.user.findMany({ where: { tenantId } });
}
</code></pre><p>b) <strong>Schema-per-tenant:</strong> Each tenant gets their own database schema (Postgres):</p><pre><code class="language-javascript">public.users
tenant_a.users
tenant_b.users
</code></pre><p>Switch schemas dynamically at runtime:</p><pre><code class="language-javascript">await prisma.$executeRawUnsafe(`SET search_path TO ${tenantId}, public`);
</code></pre><p>This offers <strong>stronger isolation</strong> but requires <strong>connection pooling</strong> and migration management (e.g., using <code>pgbouncer</code> + <code>prisma-multi-tenant</code>).</p><p></p><p><strong>4. Field-Level RBAC</strong>: Use middleware or directives to restrict field access depending on tenant role or tier (e.g., &#x201C;Pro&#x201D; customers only see analytics fields).</p><p><strong>Example with GraphQL Shield</strong></p><pre><code class="language-javascript">// permissions.ts
import { rule, shield } from &apos;graphql-shield&apos;;

const isProTenant = rule()(async (_parent, _args, ctx) =&gt; {
  return ctx.user?.plan === &apos;pro&apos;;
});

export const permissions = shield({
  Query: {
    analyticsReport: isProTenant,
  },
});
</code></pre><pre><code class="language-javascript">GraphQLModule.forRoot({
  schema: applyMiddleware(schema, permissions),
});
</code></pre><p>For Federation, RBAC can be applied <strong>both in subgraphs</strong> and at the <strong>gateway level</strong> for centralized policy enforcement.</p><p><strong>Multi-Tenant Federation Flow Summary</strong></p><pre><code>                +-------------------+
                |  Apollo Gateway   |
                |-------------------|
                |  Auth + Context   |
                |  Tenant Resolver  |
                +--------+----------+
                         |
             +-----------+------------+
             |                        |
   +---------+----------+   +---------+----------+
   |  Users Subgraph    |   |  Billing Subgraph  |
   |  Scoped by Tenant  |   |  Scoped by Tenant  |
   +--------------------+   +--------------------+
             |
         +---+---+
         | DB per|
         | Tenant|
         +-------+
</code></pre><ul><li>Gateway injects tenant ID &#x2192; shared context</li><li>Subgraphs filter data by tenantId</li><li>Optional: per-tenant schema or DB</li><li>RBAC enforced via middleware or directives</li></ul><h3 id="subgraph-considerations">Subgraph Considerations</h3><ul><li>Each service must enforce tenant scoping at repository or ORM level.</li><li>Use shared middleware to validate tenant consistency across services.</li><li>Optional: per-tenant caching to improve isolation and performance.</li></ul><h2 id="best-practices-production-tips">Best Practices &amp; Production Tips</h2><p>&#x2705; <strong>Keep tenant context immutable</strong> &#x2013; don&#x2019;t reassign or alter it mid-request.<br>&#x2705; <strong>Audit everything</strong> &#x2013; log tenant ID in every service call and DB query.<br>&#x2705; <strong>Cache isolation</strong> &#x2013; partition cache keys with tenant prefixes (<code>tenantId:resource:id</code>).<br>&#x2705; <strong>Metrics separation</strong> &#x2013; tag traces/metrics with <code>tenant_id</code> (for Prometheus/OpenTelemetry).<br>&#x2705; <strong>Schema customization</strong> &#x2013; if offering custom tenant extensions, use <code>@extends</code> or Apollo schema composition.<br>&#x2705; <strong>Automated migrations</strong> &#x2013; use CI/CD tasks to deploy schema/DB migrations per tenant.</p><h2 id="9-cross-service-caching-and-dataloader-integration">9. Cross-Service Caching and DataLoader Integration</h2><p>In large-scale <strong>federated GraphQL</strong> systems (multiple services: <code>users</code>, <code>billing</code>, <code>inventory</code>, etc.), <strong>cross-service requests</strong> often cause <strong>N+1 query problems</strong> and unnecessary <strong>network chatter</strong> between subgraphs.</p><p>To achieve optimal performance and scalability, you combine <strong>DataLoader-based batching and caching</strong> (for per-request deduplication) with <strong>gateway-level caching</strong> (for persistent response and entity caching).</p><h2 id="why-this-matters">Why This Matters</h2><p>Without loaders and caching:</p><ul><li>Each resolver triggers separate downstream calls (e.g., for each user or product).</li><li>Federated entity resolution can become network-heavy.</li><li>You lose opportunities to reuse query results or deduplicate calls.</li><li>With a well-designed DataLoader + caching setup:</li><li>Repeated entity lookups (e.g., <code>User</code> references across services) are batched into one request.</li><li>Cached entities are re-used across resolvers during a request.</li><li>Responses or entity results can be persisted in Redis or memory with TTL-based invalidation.</li></ul><p><strong>Architecture Overview</strong></p><pre><code>                        +---------------------+
                        |   Apollo Gateway    |
                        |---------------------|
                        | - Response Cache    |
                        | - Entity Cache      |
                        | - Per-request ctx   |
                        +----------+----------+
                                   |
                  +----------------+-----------------+
                  |                                  |
       +----------+----------+            +----------+----------+
       |   Users Subgraph    |            |  Orders Subgraph    |
       |---------------------|            |---------------------|
       | - DataLoader layer  |            | - DataLoader layer  |
       | - Redis cache       |            | - Redis cache       |
       +---------------------+            +---------------------+
</code></pre><h3 id="91-cross-service-dataloaderper-subgraph">9.1 Cross-Service DataLoader(per subgraph)</h3><ul><li>ach subgraph that <strong>references external entities</strong> (like <code>User</code> or <code>Product</code>) should define a <strong>DataLoader</strong>.<br>This ensures that all requests for the same entity within a single operation are batched and cached.</li></ul><p>Example &#x2014; <code>orders</code> subgraph fetching <code>User</code> from <code>users</code> subgraph</p><pre><code class="language-javascript">// loaders/user.loader.ts
import DataLoader from &apos;dataloader&apos;;
import { UserAPI } from &apos;../datasources/user.api&apos;;

export function createUserLoader(userAPI: UserAPI) {
  return new DataLoader(async (userIds: readonly string[]) =&gt; {
    const users = await userAPI.getUsersByIds(userIds as string[]);
    const userMap = new Map(users.map(u =&gt; [u.id, u]));
    return userIds.map(id =&gt; userMap.get(id));
  });
}
</code></pre><pre><code class="language-javascript">// context.ts
import { createUserLoader } from &apos;./loaders/user.loader&apos;;
import { UserAPI } from &apos;./datasources/user.api&apos;;

export function buildContext() {
  const userAPI = new UserAPI();
  return {
    loaders: {
      userLoader: createUserLoader(userAPI),
    },
  };
}
</code></pre><pre><code class="language-javascript">// order.resolver.ts
@Resolver(() =&gt; Order)
export class OrderResolver {
  @ResolveField(() =&gt; User)
  async user(@Parent() order: Order, @Context() ctx) {
    return ctx.loaders.userLoader.load(order.userId);
  }
}
</code></pre><p>&#x2705; <strong>Benefit:</strong> Within a single GraphQL request, all user lookups are batched into one network call:</p><pre><code class="language-javascript">{
  orders {
    id
    user { name }
  }
}
</code></pre><p>&#x2192; results in <strong>1 call</strong> to <code>users</code> service instead of N calls.</p><h3 id="92-gateway-level-loader-integration">9.2 Gateway-Level Loader Integration</h3><p>At the <strong>Apollo Gateway</strong>, you can inject <strong>cross-service loaders</strong> into the context for shared caching across subgraphs.<br>This is especially helpful when <strong>entity references</strong> are resolved across services.</p><pre><code>// gateway/index.ts
import { ApolloGateway, IntrospectAndCompose } from &apos;@apollo/gateway&apos;;
import { ApolloServer } from &apos;@apollo/server&apos;;
import { startStandaloneServer } from &apos;@apollo/server/standalone&apos;;
import { RedisCache } from &apos;apollo-server-cache-redis&apos;;
import DataLoader from &apos;dataloader&apos;;
import fetch from &apos;node-fetch&apos;;

const cache = new RedisCache({ host: &apos;localhost&apos;, ttl: 60 });

const gateway = new ApolloGateway({
  supergraphSdl: new IntrospectAndCompose({
    subgraphs: [
      { name: &apos;users&apos;, url: &apos;http://localhost:4001/graphql&apos; },
      { name: &apos;orders&apos;, url: &apos;http://localhost:4002/graphql&apos; },
    ],
  }),
});

const userLoader = new DataLoader(async (userIds: readonly string[]) =&gt; {
  const response = await fetch(&apos;http://localhost:4001/graphql&apos;, {
    method: &apos;POST&apos;,
    headers: { &apos;Content-Type&apos;: &apos;application/json&apos; },
    body: JSON.stringify({
      query: `query ($ids: [ID!]!) { usersByIds(ids: $ids) { id name } }`,
      variables: { ids: userIds },
    }),
  });
  const { data } = await response.json();
  return userIds.map(id =&gt; data.usersByIds.find((u: any) =&gt; u.id === id));
});

const server = new ApolloServer({
  gateway,
  cache,
  context: async ({ req }) =&gt; ({
    tenantId: req.headers[&apos;x-tenant-id&apos;],
    loaders: { userLoader },
  }),
});
</code></pre><blockquote>&#x2705; <strong>Result:</strong> Entity references across subgraphs (<code>@key(fields: &quot;id&quot;)</code>) reuse the same cached entity resolution via the gateway.</blockquote><h3 id="example-%E2%80%94-apollo-gateway-context">Example &#x2014; Apollo Gateway Context</h3><ul><li>Use <strong>DataLoader per request</strong> to batch and cache requests.</li></ul><pre><code class="language-javascript">// loaders/user.loader.ts
import DataLoader from &apos;dataloader&apos;;
export const createUserLoader = (fetchUsers) =&gt; new DataLoader(async (ids) =&gt; {
    const users = await fetchUsers(ids);
    return ids.map(id =&gt; users.find(u =&gt; u.id === id) || null);
});</code></pre><h3 id="93-caching-strategies">9.3 Caching Strategies</h3><p>Caching should happen at <strong>two levels</strong>:</p><ul><li><strong>Gateway</strong> (response-level or entity-level caching)</li><li><strong>Subgraph</strong> (request-scoped DataLoader caching or Redis persistence)</li></ul><p>Let&#x2019;s break these down &#x1F447;</p><h3 id="a-response-caching-gateway">a) Response Caching (Gateway)</h3><p>You can cache <strong>entire GraphQL responses</strong> (great for read-heavy queries) using Apollo&#x2019;s built-in <code>apollo-server-plugin-response-cache</code> or custom Redis cache.</p><pre><code class="language-javascript">import { ApolloServerPluginResponseCache } from &apos;@apollo/server-plugin-response-cache&apos;;

const server = new ApolloServer({
  gateway,
  cache,
  plugins: [
    ApolloServerPluginResponseCache({
      ttl: 30, // 30 seconds cache
      sessionId: (ctx) =&gt; ctx.tenantId, // isolate per-tenant cache
    }),
  ],
});
</code></pre><blockquote>&#x2705; <strong>Use case:</strong> Cache popular dashboards or reports that don&#x2019;t change frequently.</blockquote><h3 id="b-entity-caching-subgraph-level">b) Entity Caching (Subgraph-Level)</h3><p>Each DataLoader can store entity lookups in Redis for cross-request reuse.</p><pre><code class="language-javascript">// user.loader.ts
import DataLoader from &apos;dataloader&apos;;
import Redis from &apos;ioredis&apos;;

const redis = new Redis();

export function createUserLoader(userAPI: UserAPI) {
  return new DataLoader(async (userIds: readonly string[]) =&gt; {
    // Try cache first
    const cached = await Promise.all(userIds.map(id =&gt; redis.get(`user:${id}`)));
    const missingIds = userIds.filter((_, i) =&gt; !cached[i]);
    
    let fetched = [];
    if (missingIds.length &gt; 0) {
      fetched = await userAPI.getUsersByIds(missingIds as string[]);
      for (const user of fetched) {
        redis.set(`user:${user.id}`, JSON.stringify(user), &apos;EX&apos;, 60);
      }
    }

    const allUsers = userIds.map((id, i) =&gt; cached[i] ? JSON.parse(cached[i]!) : fetched.find(u =&gt; u.id === id));
    return allUsers;
  });
}
</code></pre><p>&#x2705; <strong>Benefit: </strong></p><ul><li>Prevents re-fetching the same user across requests.</li><li>Cache TTL ensures consistency with eventual updates.</li></ul><h3 id="c-cross-service-redis-cache-shared-cache-bus">C) Cross-Service Redis Cache (Shared Cache Bus)</h3><p>In federated setups, caches must often be <strong>shared across microservices</strong> to prevent duplication.<br>Use Redis as a <strong>central cache bus</strong> for all entity lookups.</p><p>Example topology:</p><pre><code>[Apollo Gateway]
       |
   [Redis Cache]
       |
  +----+----+
  |         |
[Users]   [Billing]
</code></pre><ul><li>Each service uses the same Redis instance or cluster.</li><li>Keys are namespaced: tenant:user:123, tenant:invoice:456.</li><li>Gateway and subgraphs can share the same cache API.</li></ul><h2 id="dcache-invalidation-strategies">d)Cache Invalidation Strategies</h2><ul><li><strong>TTL-based</strong> (simple, safe): expire keys automatically after N seconds.</li><li><strong>Event-based</strong>: publish update events to Redis channels (e.g., <code>USER_UPDATED</code>) to invalidate keys in all caches.</li><li><strong>Manual busting</strong>: Admin endpoint to clear cache when performing migrations.</li></ul><h2 id="e-combined-pattern-%E2%80%94-gateway-dataloader-redis">e) Combined Pattern &#x2014; Gateway + DataLoader + Redis</h2><p>A real-world <strong>federated caching stack</strong> typically looks like this:</p><pre><code>+------------------------+
|     Apollo Gateway     |
|------------------------|
| Response Cache (TTL)   |
| Cross-Service Loaders  |
+-----------+------------+
            |
   +--------+---------+
   |    Redis Cache   |
   +--------+---------+
            |
     +------+------+
     |   Subgraph  |
     | (Users, etc)|
     | DataLoader  |
     +-------------+
</code></pre><p><strong>Flow example:</strong></p><ul><li>Gateway receives query &#x2192; checks response cache.</li><li>If not found &#x2192; uses DataLoader to fetch federated entities.</li><li>Subgraphs use Redis-backed DataLoader to fetch &amp; cache entities.</li><li>Responses are cached per tenant at both levels.</li></ul><h2 id="f-production-tips">f) Production Tips</h2><p>&#x2705; Use <strong>per-request DataLoader</strong> &#x2014; never reuse across requests (causes data leaks).<br>&#x2705; Namespace cache keys by <strong>tenant ID</strong> to prevent cross-tenant pollution.<br>&#x2705; Monitor cache hit rates with <strong>Prometheus/OpenTelemetry</strong>.<br>&#x2705; Use <strong>Redis cluster</strong> for scale; in-memory cache for ultra-low latency.<br>&#x2705; For dynamic invalidation, prefer <strong>Pub/Sub</strong> over manual busting.<br>&#x2705; Limit cache size and use <strong>TTL with jitter</strong> (to prevent thundering herd).</p><h2 id="10-security-architecture">10. Security Architecture</h2><ul><li>Use <strong>DataLoader</strong> for N+1 batching.</li><li>Enable <strong>Response Caching</strong>.</li><li>Use <strong>query cost analysis</strong>.</li><li>Add a <strong>CDN</strong> layer (Akamai, Cloudflare) for persisted queries.</li><li>Depth &amp; complexity limits.</li><li>Disable introspection in production.</li><li>Enforce Auth via context (JWT, OAuth, session).</li><li>Use auth directives (<code>@auth</code>) or Nest guards.</li></ul><figure class="kg-card kg-code-card"><pre><code class="language-javascript">import {
    createComplexityLimitRule
} from &apos;graphql-validation-complexity&apos;;
const validationRules = [createComplexityLimitRule(1000)];</code></pre><figcaption>import.ts</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-javascript">directive @auth(role: Role!) on FIELD_DEFINITION

const server = new ApolloServer({
    schema: applyMiddleware(schema, authMiddleware),
});</code></pre><figcaption>server.ts</figcaption></figure><h2 id="12-api-governance-and-versioning">12. API Governance and Versioning</h2><p>GraphQL&#x2019;s power comes from its <strong>evolving schema</strong>, but in large organizations or federated setups, uncontrolled evolution leads to <strong>breaking changes</strong>, <strong>inconsistent schemas</strong>, and <strong>deployment chaos</strong>.</p><ul><li>API governance ensures:</li><li>&#x2705; Safe schema evolution</li><li>&#x2705; Predictable versioning and deprecation</li><li>&#x2705; Consistency across multiple teams/services</li><li>&#x2705; Continuous validation during CI/CD</li></ul><h2 id="a-core-concepts">a) Core Concepts</h2><!--kg-card-begin: html--><table data-start="918" data-end="1577" class="w-fit min-w-(--thread-content-width)"><thead data-start="918" data-end="955"><tr data-start="918" data-end="955"><th data-start="918" data-end="930" data-col-size="sm">Principle</th><th data-start="930" data-end="944" data-col-size="md">Description</th><th data-start="944" data-end="955" data-col-size="md">Example</th></tr></thead><tbody data-start="997" data-end="1577"><tr data-start="997" data-end="1131"><td data-start="997" data-end="1019" data-col-size="sm"><strong data-start="999" data-end="1018">Schema Registry</strong></td><td data-col-size="md" data-start="1019" data-end="1100">Central registry where schemas from all subgraphs are published and validated.</td><td data-col-size="md" data-start="1100" data-end="1131">Apollo Studio, GraphQL Hive</td></tr><tr data-start="1132" data-end="1283"><td data-start="1132" data-end="1162" data-col-size="sm"><strong data-start="1134" data-end="1161">Schema Validation in CI</strong></td><td data-col-size="md" data-start="1162" data-end="1253">Compares new schema with last published version and fails if breaking changes are found.</td><td data-col-size="md" data-start="1253" data-end="1283"><code data-start="1255" data-end="1281">npx rover subgraph check</code></td></tr><tr data-start="1284" data-end="1456"><td data-start="1284" data-end="1311" data-col-size="sm"><strong data-start="1286" data-end="1310">Graceful Deprecation</strong></td><td data-col-size="md" data-start="1311" data-end="1397">Mark old fields with <code data-start="1334" data-end="1366">@deprecated(reason: &quot;message&quot;)</code> to phase out features safely.</td><td data-col-size="md" data-start="1397" data-end="1456"><code data-start="1399" data-end="1454">email: String @deprecated(reason: &quot;Use contactEmail&quot;)</code></td></tr><tr data-start="1457" data-end="1577"><td data-start="1457" data-end="1484" data-col-size="sm"><strong data-start="1459" data-end="1483">Schema Documentation</strong></td><td data-col-size="md" data-start="1484" data-end="1542">Generate and share introspection or docs automatically.</td><td data-col-size="md" data-start="1542" data-end="1577"><code data-start="1544" data-end="1563">graphql-inspector</code>, <code data-start="1565" data-end="1575">graphdoc</code></td></tr></tbody></table><!--kg-card-end: html--><p><strong>Project Structure</strong></p><pre><code>/users-service
 &#x251C;&#x2500;&#x2500; src/
 &#x2502;   &#x251C;&#x2500;&#x2500; schema.graphql
 &#x2502;   &#x251C;&#x2500;&#x2500; index.ts
 &#x2502;   &#x2514;&#x2500;&#x2500; apollo.config.js
 &#x251C;&#x2500;&#x2500; package.json
 &#x251C;&#x2500;&#x2500; .env
 &#x2514;&#x2500;&#x2500; .github/workflows/schema-check.yml
</code></pre><p><strong>a) Schema Definition with Deprecation</strong></p><pre><code class="language-javascript"># src/schema.graphql
type User @key(fields: &quot;id&quot;) {
  id: ID!
  name: String!
  email: String! @deprecated(reason: &quot;Use contactEmail instead&quot;)
  contactEmail: String!
}

type Query {
  user(id: ID!): User
  users: [User!]!
}
</code></pre><p>Here we&#x2019;ve gracefully deprecated the <code>email</code> field and introduced <code>contactEmail</code>.<br>Existing clients can continue to use <code>email</code> until it&#x2019;s removed in a later version.</p><p><strong>b) Apollo Config for Schema Registry</strong></p><pre><code>// apollo.config.js
module.exports = {
  service: {
    name: &apos;users&apos;,
    localSchemaFile: &apos;./src/schema.graphql&apos;,
  },
};
</code></pre><p>Apollo key (from Apollo Studio) should be in <code>.env</code>:</p><pre><code>APOLLO_KEY=service:my-supergraph-key
APOLLO_GRAPH_REF=my-supergraph@current
</code></pre><p><strong>c) CI/CD: Schema Governance with GitHub Actions</strong></p><p>This pipeline:</p><ul><li>Installs dependencies</li><li>Checks for breaking changes</li><li>Publishes the schema if valid</li></ul><pre><code class="language-javascript"># .github/workflows/schema-check.yml
name: Validate and Publish GraphQL Schema

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  schema-validation:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 18

      - name: Install dependencies
        run: npm ci

      - name: Validate Schema against Registry
        env:
          APOLLO_KEY: ${{ secrets.APOLLO_KEY }}
          APOLLO_GRAPH_REF: my-supergraph@current
        run: npx rover subgraph check my-supergraph@current \
              --name users \
              --schema ./src/schema.graphql

      - name: Publish Schema (only on main)
        if: github.ref == &apos;refs/heads/main&apos;
        env:
          APOLLO_KEY: ${{ secrets.APOLLO_KEY }}
          APOLLO_GRAPH_REF: my-supergraph@current
        run: npx rover subgraph publish my-supergraph@current \
              --name users \
              --schema ./src/schema.graphql \
              --routing-url https://users-service.prod/graphql
</code></pre><p>&#x2705; <strong>Behavior: </strong></p><ul><li><strong>On pull request</strong> &#x2192; checks schema compatibility (fails CI if breaking changes).</li><li><strong>On main branch</strong> &#x2192; publishes the new version to Apollo Studio registry.</li><li><strong>Registry automatically</strong> <strong>composes</strong> the supergraph and <strong>alerts</strong> if composition fails.</li></ul><h3 id="schema-compatibility-rules-validated-by-apollo">Schema Compatibility Rules (Validated by Apollo)</h3><p>Rover (CLI) validates:</p><ul><li>Field removals &#x2192; &#x274C; breaking</li><li>Type changes (String &#x2192; Int) &#x2192; &#x274C; breaking</li><li>New fields &#x2192; &#x2705; safe</li><li>Deprecated fields &#x2192; &#x2705; safe, warns clients</li></ul><p>Example output:</p><pre><code>Detected one breaking change:
&#x26A0;&#xFE0F;  Field &quot;User.email&quot; was removed. Clients still use this field.
</code></pre><p>his protects downstream clients and federated subgraphs from unintentional schema drift. </p><h3 id="d-optional-graphql-hive-alternative-open-source">d) Optional: GraphQL Hive Alternative (Open Source)</h3><p>If you prefer open source:</p><pre><code>npm install -g @graphql-hive/cli
hive schema:check --project users --registry https://hive.mycompany.dev --token $HIVE_TOKEN
hive schema:publish --project users --service-url https://users-service.prod/graphql
</code></pre><p><strong>Global Governance Lifecycle</strong></p><pre><code>  &#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2510;
  &#x2502;              Developer Pushes              &#x2502;
  &#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x252C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;
                  &#x2502;
        1&#xFE0F;&#x20E3; Rover CI runs schema check
                  &#x2502;
        2&#xFE0F;&#x20E3; Compares against registry
                  &#x2502;
        3&#xFE0F;&#x20E3; Detects breaking changes
                  &#x2502;
        4&#xFE0F;&#x20E3; Publishes valid schemas
                  &#x2502;
        5&#xFE0F;&#x20E3; Apollo Studio composes supergraph
                  &#x2502;
        6&#xFE0F;&#x20E3; Gateway auto-updates configuration
</code></pre><h2 id="best-practices">Best Practices</h2><p>&#x2705; Always publish schemas through CI, never manually.<br>&#x2705; Deprecate &#x2192; Warn &#x2192; Remove (after 2 release cycles).<br>&#x2705; Use <strong>Schema Linting</strong> (<code>graphql-schema-linter</code>) in pre-commit hooks.<br>&#x2705; Tag every published schema (<code>@staging</code>, <code>@prod</code>) for rollback support.<br>&#x2705; Generate and publish <strong>changelogs</strong> from schema diffs.<br>&#x2705; Monitor schema usage via <strong>Apollo Studio Metrics</strong> or Hive usage reports.</p><h3 id="%E2%9C%85-tldr-%E2%80%93-one-global-flow">&#x2705; TL;DR &#x2013; One Global Flow</h3><ul><li>Developer modifies schema &#x2192; commits PR.</li><li>CI uses Rover/Hive &#x2192; validates against registry.</li><li>If safe &#x2192; schema published &#x2192; supergraph updated.</li><li>Deprecated fields tracked and reported.</li><li>Gateway auto-pulls new supergraph SDL.</li></ul><h2 id="13-pagination-filtering-sorting">13. Pagination, Filtering, Sorting</h2><p>GraphQL&#x2019;s flexibility allows clients to define <em>exactly</em> what data they need &#x2014; but this also means you must carefully design your <strong>query patterns</strong>, <strong>input arguments</strong>, and <strong>cursor logic</strong> to keep APIs efficient, predictable, and secure.</p><p><strong>a) A robust backend should support:</strong></p><!--kg-card-begin: html--><table data-start="852" data-end="1328" class="w-fit min-w-(--thread-content-width)"><thead data-start="852" data-end="877"><tr data-start="852" data-end="877"><th data-start="852" data-end="862" data-col-size="sm">Feature</th><th data-start="862" data-end="877" data-col-size="md">Description</th></tr></thead><tbody data-start="906" data-end="1328"><tr data-start="906" data-end="1021"><td data-start="906" data-end="950" data-col-size="sm"><strong data-start="908" data-end="949">Cursor-based pagination (Relay style)</strong></td><td data-col-size="md" data-start="950" data-end="1021">Ensures stable, forward/backward traversal across ordered datasets.</td></tr><tr data-start="1022" data-end="1125"><td data-start="1022" data-end="1038" data-col-size="sm"><strong data-start="1024" data-end="1037">Filtering</strong></td><td data-col-size="md" data-start="1038" data-end="1125">Dynamic field-based filtering using AND/OR conditions, ranges, and partial matches.</td></tr><tr data-start="1126" data-end="1199"><td data-start="1126" data-end="1140" data-col-size="sm"><strong data-start="1128" data-end="1139">Sorting</strong></td><td data-col-size="md" data-start="1140" data-end="1199">Multiple field sort criteria with direction (ASC/DESC).</td></tr><tr data-start="1200" data-end="1328"><td data-start="1200" data-end="1229" data-col-size="sm"><strong data-start="1202" data-end="1228">Consistency &amp; security</strong></td><td data-col-size="md" data-start="1229" data-end="1328">Prevent over-fetching and ensure stable cursors using opaque tokens (e.g., Base64 encoded IDs).</td></tr></tbody></table><!--kg-card-end: html--><p><strong>b) Exemple</strong></p><p>Below is a <strong>complete and self-contained implementation</strong> showing a production-ready design using <strong>NestJS + Apollo + Prisma</strong> with <strong>cursor-based pagination</strong>, <strong>filtering</strong>, and <strong>sorting</strong>, all in a single code block.</p><pre><code class="language-javascript">// src/posts/posts.module.ts
import { Module, Query, Resolver, Args, Field, ObjectType, InputType, registerEnumType } from &apos;@nestjs/graphql&apos;;
import { PrismaClient } from &apos;@prisma/client&apos;;
import { Injectable } from &apos;@nestjs/common&apos;;
import { GraphQLScalarType } from &apos;graphql&apos;;
import { Kind } from &apos;graphql/language&apos;;
import { Buffer } from &apos;buffer&apos;;

// --- ENUMS FOR SORTING ---
export enum SortOrder {
  ASC = &apos;asc&apos;,
  DESC = &apos;desc&apos;,
}
registerEnumType(SortOrder, { name: &apos;SortOrder&apos; });

// --- CURSOR SCALAR (OPAQUE BASE64 ID) ---
export const CursorScalar = new GraphQLScalarType({
  name: &apos;Cursor&apos;,
  description: &apos;Opaque Base64-encoded cursor ID&apos;,
  serialize(value: string) {
    return Buffer.from(value).toString(&apos;base64&apos;);
  },
  parseValue(value: string) {
    return Buffer.from(value, &apos;base64&apos;).toString(&apos;ascii&apos;);
  },
  parseLiteral(ast) {
    return ast.kind === Kind.STRING ? Buffer.from(ast.value, &apos;base64&apos;).toString(&apos;ascii&apos;) : null;
  },
});

// --- GRAPHQL OBJECT TYPES ---
@ObjectType()
class Post {
  @Field(() =&gt; String)
  id: string;

  @Field()
  title: string;

  @Field()
  createdAt: Date;

  @Field()
  authorId: string;
}

@ObjectType()
class PageInfo {
  @Field(() =&gt; CursorScalar, { nullable: true })
  endCursor?: string;

  @Field(() =&gt; Boolean)
  hasNextPage: boolean;
}

@ObjectType()
class PostConnection {
  @Field(() =&gt; [Post])
  edges: Post[];

  @Field(() =&gt; PageInfo)
  pageInfo: PageInfo;
}

// --- INPUT TYPES FOR FILTERING &amp; SORTING ---
@InputType()
class PostFilterInput {
  @Field({ nullable: true })
  titleContains?: string;

  @Field({ nullable: true })
  authorId?: string;

  @Field({ nullable: true })
  createdAfter?: Date;

  @Field({ nullable: true })
  createdBefore?: Date;
}

@InputType()
class PostSortInput {
  @Field(() =&gt; String)
  field: keyof Post;

  @Field(() =&gt; SortOrder)
  order: SortOrder;
}

// --- SERVICE LAYER ---
@Injectable()
class PostService {
  private prisma = new PrismaClient();

  async findManyPaginated(args: {
    first?: number;
    after?: string;
    filters?: PostFilterInput;
    sort?: PostSortInput;
  }) {
    const take = args.first ?? 10;

    const where: any = {};
    if (args.filters?.titleContains) where.title = { contains: args.filters.titleContains };
    if (args.filters?.authorId) where.authorId = args.filters.authorId;
    if (args.filters?.createdAfter || args.filters?.createdBefore)
      where.createdAt = {
        ...(args.filters.createdAfter &amp;&amp; { gte: args.filters.createdAfter }),
        ...(args.filters.createdBefore &amp;&amp; { lte: args.filters.createdBefore }),
      };

    const orderBy = args.sort
      ? { [args.sort.field]: args.sort.order }
      : { createdAt: &apos;desc&apos; };

    const cursor = args.after ? { id: args.after } : undefined;

    const items = await this.prisma.post.findMany({
      take: take + 1, // fetch one extra for hasNextPage
      where,
      orderBy,
      ...(cursor &amp;&amp; { skip: 1, cursor }),
    });

    const hasNextPage = items.length &gt; take;
    const edges = hasNextPage ? items.slice(0, -1) : items;
    const endCursor = edges.length &gt; 0 ? edges[edges.length - 1].id : null;

    return {
      edges,
      pageInfo: {
        endCursor,
        hasNextPage,
      },
    };
  }
}

// --- RESOLVER ---
@Resolver(() =&gt; Post)
class PostResolver {
  constructor(private readonly postService: PostService) {}

  @Query(() =&gt; PostConnection)
  async posts(
    @Args(&apos;first&apos;, { type: () =&gt; Number, nullable: true }) first: number,
    @Args(&apos;after&apos;, { type: () =&gt; CursorScalar, nullable: true }) after?: string,
    @Args(&apos;filters&apos;, { type: () =&gt; PostFilterInput, nullable: true }) filters?: PostFilterInput,
    @Args(&apos;sort&apos;, { type: () =&gt; PostSortInput, nullable: true }) sort?: PostSortInput,
  ): Promise&lt;PostConnection&gt; {
    return this.postService.findManyPaginated({ first, after, filters, sort });
  }
}

// --- MODULE EXPORT ---
@Module({
  providers: [PostResolver, PostService],
})
export class PostsModule {}
</code></pre><p><strong>c) Explanation of Key Concepts</strong></p><!--kg-card-begin: html--><table data-start="5619" data-end="6365" class="w-fit min-w-(--thread-content-width)"><thead data-start="5619" data-end="5654"><tr data-start="5619" data-end="5654"><th data-start="5619" data-end="5629" data-col-size="sm">Concept</th><th data-start="5629" data-end="5643" data-col-size="md">Description</th><th data-start="5643" data-end="5654" data-col-size="sm">Benefit</th></tr></thead><tbody data-start="5694" data-end="6365"><tr data-start="5694" data-end="5836"><td data-start="5694" data-end="5713" data-col-size="sm"><strong data-start="5696" data-end="5712">CursorScalar</strong></td><td data-col-size="md" data-start="5713" data-end="5795">Base64-encoded unique ID ensures opaque cursors (clients can&#x2019;t infer DB state).</td><td data-col-size="sm" data-start="5795" data-end="5836">Stable pagination across data changes</td></tr><tr data-start="5837" data-end="5940"><td data-start="5837" data-end="5860" data-col-size="sm"><strong data-start="5839" data-end="5859">PageInfo pattern</strong></td><td data-col-size="md" data-start="5860" data-end="5911">Mirrors Relay spec (<code data-start="5882" data-end="5895">hasNextPage</code>, <code data-start="5897" data-end="5908">endCursor</code>).</td><td data-col-size="sm" data-start="5911" data-end="5940">Predictable pagination UX</td></tr><tr data-start="5941" data-end="6022"><td data-start="5941" data-end="5963" data-col-size="sm"><strong data-start="5943" data-end="5962">Dynamic filters</strong></td><td data-col-size="md" data-start="5963" data-end="5995">Simple AND condition builder.</td><td data-col-size="sm" data-start="5995" data-end="6022">Reusable and composable</td></tr><tr data-start="6023" data-end="6126"><td data-start="6023" data-end="6049" data-col-size="sm"><strong data-start="6025" data-end="6048">Sorting abstraction</strong></td><td data-col-size="md" data-start="6049" data-end="6092">Allows arbitrary sort field + direction.</td><td data-col-size="sm" data-start="6092" data-end="6126">Prevents hardcoded order logic</td></tr><tr data-start="6127" data-end="6240"><td data-start="6127" data-end="6150" data-col-size="sm"><strong data-start="6129" data-end="6149">Take + 1 pattern</strong></td><td data-col-size="md" data-start="6150" data-end="6206">Fetch one extra record to detect if more pages exist.</td><td data-col-size="sm" data-start="6206" data-end="6240">Efficient pagination detection</td></tr><tr data-start="6241" data-end="6365"><td data-start="6241" data-end="6273" data-col-size="sm"><strong data-start="6243" data-end="6272">Single resolver signature</strong></td><td data-col-size="md" data-start="6273" data-end="6335">Combines pagination, filtering, and sorting into one query.</td><td data-col-size="sm" data-start="6335" data-end="6365">Clean and maintainable API</td></tr></tbody></table><!--kg-card-end: html--><p><strong>d) Example Query</strong></p><pre><code class="language-javascript">query {
  posts(
    first: 5
    after: &quot;QmFzZTY0Q3Vyc29ySWQxMjM=&quot;
    filters: { titleContains: &quot;GraphQL&quot;, createdAfter: &quot;2025-01-01&quot; }
    sort: { field: &quot;createdAt&quot;, order: DESC }
  ) {
    edges {
      id
      title
      createdAt
    }
    pageInfo {
      endCursor
      hasNextPage
    }
  }
}
</code></pre><h2 id="production-best-practices">Production Best Practices</h2><p>&#x2705; Use <strong>opaque cursors</strong> (never expose numeric offsets).<br>&#x2705; Always <strong>enforce pagination limits</strong> (e.g., <code>max: 100</code>).<br>&#x2705; <strong>Index fields</strong> used in sorting or filtering.<br>&#x2705; <strong>Avoid offset pagination</strong> for large datasets (unstable under concurrent writes).<br>&#x2705; Implement <strong>totalCount</strong> via cached count queries if needed.<br>&#x2705; Combine with <strong>DataLoader</strong> to batch entity lookups per page.</p><h2 id="14-subscriptions-and-real-time-graphql">14. Subscriptions and Real-time GraphQL</h2><h3 id="a-overview">a) Overview</h3><p>GraphQL Subscriptions allow clients to receive <strong>live updates</strong> whenever data changes, using WebSockets or other persistent transports.<br>They&#x2019;re essential for use cases like:</p><ul><li>Live dashboards and metrics</li><li>Chat and notifications</li><li>Collaborative document editing</li><li>Realtime feed updates (posts, likes, etc.)</li><li>In production, a robust setup requires:</li></ul><!--kg-card-begin: html--><table data-start="825" data-end="1346" class="w-fit min-w-(--thread-content-width)"><thead data-start="825" data-end="850"><tr data-start="825" data-end="850"><th data-start="825" data-end="835" data-col-size="sm">Concern</th><th data-start="835" data-end="850" data-col-size="md">Description</th></tr></thead><tbody data-start="879" data-end="1346"><tr data-start="879" data-end="972"><td data-start="879" data-end="899" data-col-size="sm"><strong data-start="881" data-end="898">Pub/Sub layer</strong></td><td data-col-size="md" data-start="899" data-end="972">Event propagation across multiple instances (Redis, Kafka, RabbitMQ).</td></tr><tr data-start="973" data-end="1048"><td data-start="973" data-end="1002" data-col-size="sm"><strong data-start="975" data-end="1001">Context authentication</strong></td><td data-col-size="md" data-start="1002" data-end="1048">Validate user sessions at connection time.</td></tr><tr data-start="1049" data-end="1144"><td data-start="1049" data-end="1072" data-col-size="sm"><strong data-start="1051" data-end="1071">Filtering events</strong></td><td data-col-size="md" data-start="1072" data-end="1144">Send updates only to relevant subscribers (based on args or tenant).</td></tr><tr data-start="1145" data-end="1243"><td data-start="1145" data-end="1163" data-col-size="sm"><strong data-start="1147" data-end="1162">Scalability</strong></td><td data-col-size="md" data-start="1163" data-end="1243">Ensure Pub/Sub broker synchronizes events between horizontally scaled nodes.</td></tr><tr data-start="1244" data-end="1346"><td data-start="1244" data-end="1272" data-col-size="sm"><strong data-start="1246" data-end="1271">Transport abstraction</strong></td><td data-col-size="md" data-start="1272" data-end="1346">Typically <code data-start="1284" data-end="1296">graphql-ws</code> or <code data-start="1300" data-end="1328">subscriptions-transport-ws</code> for WebSockets.</td></tr></tbody></table><!--kg-card-end: html--><h3 id="b-example-nestjs-apollo-server-redis-pubsub"> b) Example (NestJS + Apollo Server + Redis PubSub)</h3><p>The following single code block shows a <strong>complete production-ready setup</strong> with:</p><ul><li>GraphQL subscription for <code>postCreated</code></li><li>Redis PubSub for cross-instance broadcasting</li><li>Auth-aware WebSocket context</li><li>Dynamic filtering by tenant/user</li><li>Integration into NestJS GraphQL module</li></ul><pre><code class="language-javascript">// src/posts/posts.module.ts
import { Module, Resolver, Query, Mutation, Args, Subscription, ObjectType, Field, InputType } from &apos;@nestjs/graphql&apos;;
import { Injectable } from &apos;@nestjs/common&apos;;
import { PubSub } from &apos;graphql-subscriptions&apos;;
import Redis from &apos;ioredis&apos;;
import { RedisPubSub } from &apos;graphql-redis-subscriptions&apos;;
import { GraphQLModule } from &apos;@nestjs/graphql&apos;;
import { ApolloDriver, ApolloDriverConfig } from &apos;@nestjs/apollo&apos;;
import { ApolloServerPluginLandingPageLocalDefault } from &apos;@apollo/server/plugin/landingPage/default&apos;;
import { ExecutionContext } from &apos;@nestjs/common&apos;;
import { GqlExecutionContext } from &apos;@nestjs/graphql&apos;;

// --- REDIS PUBSUB CONFIG ---
const pubSub = new RedisPubSub({
  publisher: new Redis({ host: &apos;localhost&apos;, port: 6379 }),
  subscriber: new Redis({ host: &apos;localhost&apos;, port: 6379 }),
});

// --- GRAPHQL TYPES ---
@ObjectType()
class Post {
  @Field()
  id: string;

  @Field()
  title: string;

  @Field()
  content: string;

  @Field()
  authorId: string;

  @Field()
  createdAt: Date;
}

@InputType()
class CreatePostInput {
  @Field()
  title: string;

  @Field()
  content: string;
}

// --- SERVICE LAYER ---
@Injectable()
class PostService {
  private posts: Post[] = [];

  async createPost(data: CreatePostInput, userId: string): Promise&lt;Post&gt; {
    const newPost: Post = {
      id: `${this.posts.length + 1}`,
      title: data.title,
      content: data.content,
      authorId: userId,
      createdAt: new Date(),
    };
    this.posts.push(newPost);

    // Publish event for subscribers
    await pubSub.publish(&apos;postCreated&apos;, { postCreated: newPost });
    return newPost;
  }

  async findAll(): Promise&lt;Post[]&gt; {
    return this.posts;
  }
}

// --- RESOLVER ---
@Resolver(() =&gt; Post)
class PostResolver {
  constructor(private readonly postService: PostService) {}

  @Query(() =&gt; [Post])
  async posts() {
    return this.postService.findAll();
  }

  @Mutation(() =&gt; Post)
  async createPost(
    @Args(&apos;data&apos;) data: CreatePostInput,
    @Args(&apos;userId&apos;) userId: string,
  ) {
    return this.postService.createPost(data, userId);
  }

  @Subscription(() =&gt; Post, {
    filter: (payload, variables, context) =&gt; {
      // Example: filter events only for posts authored by user
      return payload.postCreated.authorId === context.user?.id;
    },
    resolve: (value) =&gt; value.postCreated,
  })
  postCreated() {
    return pubSub.asyncIterator(&apos;postCreated&apos;);
  }
}

// --- GRAPHQL MODULE ---
@Module({
  imports: [
    GraphQLModule.forRoot&lt;ApolloDriverConfig&gt;({
      driver: ApolloDriver,
      autoSchemaFile: true,
      playground: false,
      plugins: [ApolloServerPluginLandingPageLocalDefault()],
      subscriptions: {
        &apos;graphql-ws&apos;: {
          onConnect: async (ctx) =&gt; {
            const token = ctx.connectionParams?.authorization || null;
            // Verify token (mocked example)
            const user = token ? { id: &apos;user-123&apos;, name: &apos;John Doe&apos; } : null;
            return { user };
          },
        },
      },
      context: ({ req, extra }) =&gt; {
        // Context for both HTTP and WS
        if (extra &amp;&amp; extra.user) return { user: extra.user };
        const auth = req?.headers?.authorization;
        return { user: auth ? { id: &apos;user-123&apos;, name: &apos;John Doe&apos; } : null };
      },
    }),
  ],
  providers: [PostResolver, PostService],
})
export class PostsModule {}
</code></pre><h3 id="c-explanation">c) Explanation</h3><!--kg-card-begin: html--><table data-start="5135" data-end="5682" class="w-fit min-w-(--thread-content-width)"><thead data-start="5135" data-end="5160"><tr data-start="5135" data-end="5160"><th data-start="5135" data-end="5145" data-col-size="sm">Feature</th><th data-start="5145" data-end="5160" data-col-size="md">Description</th></tr></thead><tbody data-start="5189" data-end="5682"><tr data-start="5189" data-end="5276"><td data-start="5189" data-end="5207" data-col-size="sm"><strong data-start="5191" data-end="5206">RedisPubSub</strong></td><td data-start="5207" data-end="5276" data-col-size="md">Allows messages to be broadcast across multiple server instances.</td></tr><tr data-start="5277" data-end="5354"><td data-start="5277" data-end="5307" data-col-size="sm"><strong data-start="5279" data-end="5306">Subscriptions decorator</strong></td><td data-start="5307" data-end="5354" data-col-size="md">Creates a live channel using PubSub topics.</td></tr><tr data-start="5355" data-end="5439"><td data-start="5355" data-end="5377" data-col-size="sm"><strong data-start="5357" data-end="5376"><code data-start="5359" data-end="5367">filter</code> option</strong></td><td data-start="5377" data-end="5439" data-col-size="md">Prevents irrelevant events from being sent to subscribers.</td></tr><tr data-start="5440" data-end="5517"><td data-start="5440" data-end="5463" data-col-size="sm"><strong data-start="5442" data-end="5462"><code data-start="5444" data-end="5455">onConnect</code> hook</strong></td><td data-start="5463" data-end="5517" data-col-size="md">Authenticates WebSocket clients before connection.</td></tr><tr data-start="5518" data-end="5595"><td data-start="5518" data-end="5541" data-col-size="sm"><strong data-start="5520" data-end="5540">Shared <code data-start="5529" data-end="5538">context</code></strong></td><td data-start="5541" data-end="5595" data-col-size="md">Keeps user and tenant info available in resolvers.</td></tr><tr data-start="5596" data-end="5682"><td data-start="5596" data-end="5620" data-col-size="sm"><strong data-start="5598" data-end="5619">Unified transport</strong></td><td data-start="5620" data-end="5682" data-col-size="md">Supports both <code data-start="5636" data-end="5642">http</code> and <code data-start="5647" data-end="5659">graphql-ws</code> contexts seamlessly.</td></tr></tbody></table><!--kg-card-end: html--><h3 id="d-example-operations">d) Example &#xA0;Operations</h3><p><strong>Subscribe</strong></p><pre><code>subscription {
  postCreated {
    id
    title
    authorId
    createdAt
  }
}
</code></pre><p><strong>Mutate (to trigger event)</strong></p><pre><code>mutation {
  createPost(data: { title: &quot;Hello Subscriptions&quot;, content: &quot;GraphQL Rocks!&quot; }, userId: &quot;user-123&quot;) {
    id
    title
  }
}
</code></pre><h3 id="e-production-best-practices">e) Production Best Practices</h3><p>&#x2705; Use <strong>Redis or Kafka</strong> for distributed pub/sub scaling.<br>&#x2705; Authenticate and authorize WebSocket connections securely.<br>&#x2705; Limit subscription count per user to prevent DoS.<br>&#x2705; Apply <strong>TTL</strong> and <strong>message filtering</strong> for event performance.<br>&#x2705; Use <strong>dedicated gateway or subscription service</strong> under load.<br>&#x2705; Integrate <strong>metrics &amp; tracing</strong> with OpenTelemetry for monitoring.</p><h2 id="15-testing-strategy">15. Testing Strategy</h2><ul><li>Unit: Test services.</li><li>Integration: Run resolvers with in-memory schema.</li><li>E2E: Test full API + DB.</li><li>Contract: Validate schema compatibility.</li></ul><hr><h2 id="16-observability">16. Observability</h2><ul><li>Use OpenTelemetry instrumentation.</li><li>Add tracing IDs in logs.</li><li>Expose Prometheus metrics: latency per resolver.</li></ul><hr><h2 id="17-%E2%80%94-example-cicd-pipeline-for-federated-graphql">17 &#x2014; Example CI/CD Pipeline for Federated GraphQL</h2><h3 id="goals">Goals</h3><ul><li>Automate build, test, and deploy for <strong>gateway</strong> and <strong>subgraphs</strong>.</li><li>Validate schema compatibility before deployment.</li><li>Ensure rollback in case of failed federation compatibility.</li></ul><h3 id="example-github-actions">Example (GitHub Actions)</h3><figure class="kg-card kg-code-card"><pre><code>name: Federated GraphQL CI / CD

on:
    push:
    branches: [main]

jobs:
    test:
    runs - on: ubuntu - latest
strategy:
    matrix:
    service: [users, posts, comments]
steps:
    -uses: actions / checkout @v3 -
    name: Set up Node
uses: actions / setup - node @v3
with:
    node - version: &apos;18&apos; -
    run: npm ci -
    run: npm run test


validate - schema:
    runs - on: ubuntu - latest
needs: test
steps:
    -uses: actions / checkout @v3 -
    name: Validate Federation
run: |
    npx apollo service: check--endpoint = http: //gateway:4000/graphql


    deploy:
    runs - on: ubuntu - latest
needs: [test, validate - schema]
steps:
    -uses: actions / checkout @v3 -
    name: Build and Deploy Users Service
run: |
    docker build - t users - service. / services / users
docker push myregistry / users - service: latest
kubectl apply - f k8s / users - deployment.yaml -
    name: Deploy Gateway
run: |
    docker build - t gateway. / gateway
docker push myregistry / gateway: latest
kubectl apply - f k8s / gateway - deployment.yaml</code></pre><figcaption>ci.yaml</figcaption></figure><h3 id="notes">Notes</h3><ul><li>Schema validation ensures <strong>federation compatibility</strong> across services.</li><li>Deploy subgraphs independently; gateway deploy last or with rolling updates.</li><li>Rollback on schema validation failure prevents breaking clients.</li></ul><h2 id="18-deployment-strategies">18. Deployment Strategies</h2><ul><li>Apollo Gateway in Kubernetes.</li><li>Federation services as independent pods.</li><li>Use horizontal pod autoscaling.</li><li>Cache persisted queries at CDN level.</li></ul><hr><h2 id="19-developer-experience">19. Developer Experience</h2><ul><li>GraphQL Code Generator &#x2192; type safety.</li><li>ESLint + Prettier + Husky.</li><li>Doc generation via GraphQL Voyager or GraphiQL Explorer.</li></ul><hr><h2 id="20-error-handling-logging">20. Error Handling &amp; Logging</h2><ul><li>Standardize error codes.</li><li>Hide internal errors in production.</li><li>Log context and userId.</li></ul><hr><h2 id="21-performance-benchmarks">21. Performance Benchmarks</h2><p>Use tools like <strong><a href="https://studio.apollographql.com/">Apollo studio</a></strong>, <strong><a href="https://github.com/hasura/graphql-bench">graphql-bench</a></strong> and <strong><a href="https://www.artillery.io/">Artillery</a></strong>.</p><hr><h2 id="23-production-checklist">23. Production Checklist</h2><p>&#x2705; Schema registry + versioning<br>&#x2705; Query complexity limits<br>&#x2705; Dataloader cache<br>&#x2705; Persisted queries<br>&#x2705; Structured logging<br>&#x2705; Observability stack<br>&#x2705; CI/CD validation</p><hr><h2 id="24-tools-resources">24. Tools &amp; Resources</h2><ul><li><strong>Apollo Server</strong>: A spec-compliant GraphQL server compatible with any GraphQL client, including Apollo Client. It&apos;s a production-ready solution for building self-documenting GraphQL APIs that can use data from any source. <a href="https://www.apollographql.com/docs/apollo-server">apollographql.com</a></li><li><strong>Apollo Federation</strong>: An architecture for declaratively composing APIs into a unified graph. Each team can own their slice of the graph independently, empowering them to deliver autonomously and incrementally. <a href="https://www.apollographql.com/docs/graphos/schema-design/federated-schemas/federation">apollographql.com</a></li><li><strong>Apollo Gateway</strong>: The gateway sits in front of subgraphs, executing operations across them. It processes the supergraph schema and creates query plans for incoming requests. <a href="https://www.apollographql.com/docs/federation/v1/gateway">apollographql.com</a></li><li><strong>NestJS GraphQL</strong>: A progressive Node.js framework that provides a simple and flexible way to build GraphQL APIs using the <code>@nestjs/graphql</code> module. <a href="https://docs.nestjs.com/graphql/quick-start">docs.nestjs.com</a></li></ul><hr><h3 id="data-access-orm">Data Access &amp; ORM</h3><p><strong>Prisma</strong>: A next-generation Node.js and TypeScript ORM for PostgreSQL, MySQL, SQL Server, SQLite, MongoDB, and CockroachDB. It provides type-safety, auto-completion, and a powerful query engine. <a href="https://www.prisma.io/docs">Prisma</a></p><p><strong>TypeORM</strong>: An ORM for TypeScript and JavaScript that supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, and WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova, and Electron platforms. <a href="https://typeorm.io">typeorm.io</a></p><p><strong>MikroORM</strong>: A TypeScript ORM for Node.js based on Data Mapper, Unit of Work, and Identity Map patterns. <a href="https://mikro-orm.io/docs/quick-start">mikro-orm.io</a></p><hr><h3 id="developer-tools">Developer Tools</h3><p><strong><a href="https://the-guild.dev/graphql/codegen">GraphQL Code Generator</a></strong>: A plugin-based tool that helps you get the best out of your GraphQL stack. From back-end to front-end, it automates the generation of typed Queries, Mutations, and Subscriptions for React, Vue, Angular, Next.js, Svelte, whether you are using Apollo Client, URQL, or React Query. <a href="https://the-guild.dev/graphql/codegen/docs/getting-started">The Guild</a></p><p><strong><a href="https://github.com/maticzav/graphql-shield">GraphQL Shield</a></strong>: A library for creating permission layers for your GraphQL schema using a functional approach.</p><p><strong><a href="https://github.com/graphile/depth-limit">GraphQL Depth Limit</a></strong>: A middleware for limiting the depth of queries to prevent malicious or accidental deep queries.</p><p><strong><a href="https://www.npmjs.com/package/graphql-query-complexity">GraphQL Query Complexity</a></strong>: A utility to calculate the complexity of a GraphQL query to prevent overly expensive operations.</p><h3 id="observability-monitoring">Observability &amp; Monitoring</h3><p><strong><a href="https://opentelemetry.io/">OpenTelemetry</a></strong>: An open-source project that provides APIs, libraries, agents, and instrumentation to enable observability for applications. </p><p><strong><a href="https://prometheus.io/">Prometheus</a></strong>: An open-source system monitoring and alerting toolkit designed for reliability and scalability.</p><p><strong><a href="https://www.jaegertracing.io/">Jaeger</a></strong>: An open-source, end-to-end distributed tracing system.</p><hr><h3 id="integration-ecosystem">Integration &amp; Ecosystem</h3><p><strong><a href="https://the-guild.dev/graphql/mesh">GraphQL Mesh</a></strong>: A tool that allows you to access any data source as GraphQL, including REST APIs, SOAP, gRPC, and more.</p><p><a href="https://studio.apollographql.com/">Apollo Studio</a>: A platform for building, testing, and managing GraphQL APIs.</p>]]></content:encoded></item><item><title><![CDATA[GraphQL  Backend - Schema-first vs Code-first]]></title><description><![CDATA[<p>GraphQL is a query language for API created by Facebook in 2012 and open-sourced in 2015. It provides a set of features that allows building Back-end and a very optimized way for Data fetching. According to <code>https://graphql.org</code> GraphQL provides a complete and understandable description of the data in</p>]]></description><link>https://blog.foujeupavel.com/graphql-backend-schema-first-vs-code-first/</link><guid isPermaLink="false">631126d3316abd7fd59e8f5b</guid><category><![CDATA[APIs]]></category><category><![CDATA[Backend]]></category><category><![CDATA[Graphql]]></category><category><![CDATA[Typescript]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Sun, 08 Jan 2023 02:28:25 GMT</pubDate><media:content url="https://blog.foujeupavel.com/content/images/2022/12/gql.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.foujeupavel.com/content/images/2022/12/gql.png" alt="GraphQL  Backend - Schema-first vs Code-first"><p>GraphQL is a query language for API created by Facebook in 2012 and open-sourced in 2015. It provides a set of features that allows building Back-end and a very optimized way for Data fetching. According to <code>https://graphql.org</code> GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. </p><p>To do this, GraphQL is centered around an element called <strong>The GraphQL Schema</strong> which is the Contract between the Back-end service and the Front-end on how to consume the service and what can be served by the service. <br><br>As you will have understood, everything goes through this GraphQL schema. &#x1F609; Now this being said, let&apos;s see what are the different ways to build it</p><p>As a backend developer, I sometimes had to wonder which approach to adopt when I had to build a GraphQL Backend. When I started working on GraphQL, I used to work with the standard way to do this task, what is called the &quot;<strong>Schema-first&quot; </strong>approach where the GraphQL Schema defining type, fields, resolvers function, and relationships between data are defined first to say which fields can be exposed by the service and which functions resolvers can be called to ask for those fields and which parameters are required by each resolver. </p><p>On the other hand, this approach is not the only way to build a GraphQL service since the <strong>&quot;Code-first&quot;</strong> approach can be another way to build a GraphQL API. In this way, you only need to write the resolvers function needed to serve a specific need with some annotations, and a build tool will compile the schema and the SDL based on types and provided annotations. We will dive deeper into it later. let&apos;s jump into each approach now. </p><h2 id="1-schema-first">1. Schema-first</h2><p>This way of building GraphQL services has been the only way to build a GraphQL backend at the beginning and it is based on a simple concept, the &quot;<strong>Contract</strong>&quot; &#x261D;&#xFE0F;</p><p>The &quot;Contract&quot; here represents an agreement between the Front-end and the Back-end on how Data are defined (field names, resolvers functions, available fields, etc ..) and how all the Frontend can consume those Data. This way, Front-end developers can work asynchronously when the GraphQL schema is ready. To do so, we use the SDL (Schema D&#xE9;finition Language) to define the schema and then create all the resolver functions that execute and return data at runtime. Therefore, the GraphQL schema is like a <strong>Blueprint</strong> of a GraphQL API.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">type Query {
  welcome: String!
  users: [User!]!
}
  
type User {
 _id: ID!
 firstName: String!
 lastName: String!
 email:	String!
}
</code></pre><figcaption>schema.graphql</figcaption></figure><p>This pattern is used by many popular GraphQL servers. let&apos;s see this with a real example. To see this in the real world, let&apos;s build a simple service using <a href="https://www.apollographql.com/">Apollo Server</a> and Typescript. Let&apos;s follow the same approach as shown on their website</p><p><strong>Step 1: Create a new project</strong></p><p>Open your favorite terminal and create a folder </p><pre><code class="language-javascript">mkdir demo-schema-first 
cd demo-schema-first</code></pre><p> then run the following commands <code>yarn init --yes</code> to initialize a node.js project using Yarn as a package manager. this will create a <code>package.json</code> </p><p><strong>Step 2: Install dependencies</strong></p><ul><li><code>yarn add @apollo/server graphql</code> to install useful packages</li><li><code>yarn add -D typescript nodemon @types/node</code> to install typescript and corresponding types</li><li>Now run <code>touch tsconfig.json</code> to create a <code>tsconfig.json</code> and paste the following code into </li></ul><figure class="kg-card kg-code-card"><pre><code class="language-javascript">{
  &quot;compilerOptions&quot;: {
    &quot;rootDirs&quot;: [&quot;src&quot;],
    &quot;outDir&quot;: &quot;dist&quot;,
    &quot;lib&quot;: [&quot;es2020&quot;],
    &quot;target&quot;: &quot;es2020&quot;,
    &quot;module&quot;: &quot;esnext&quot;,
    &quot;moduleResolution&quot;: &quot;node&quot;,
    &quot;esModuleInterop&quot;: true,
    &quot;types&quot;: [&quot;node&quot;]
  }
}</code></pre><figcaption>tsconfig.json</figcaption></figure><p>Add the following in your pakage.json</p><pre><code class="language-javascript">&quot;type&quot;: &quot;module&quot;, 
&quot;scripts&quot;: {
   &quot;compile&quot;: &quot;tsc&quot;,
   &quot;start&quot;: &quot;npm run compile &amp;&amp; nodemon ./dist/index.js&quot;
  }</code></pre><p>Our package.json will look like this </p><pre><code>{
  &quot;name&quot;: &quot;demo-schema-first&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;license&quot;: &quot;MIT&quot;,
  &quot;type&quot;: &quot;module&quot;,
  &quot;scripts&quot;: {
    &quot;compile&quot;: &quot;tsc&quot;,
    &quot;start&quot;: &quot;npm run compile &amp;&amp; node ./dist/index.js&quot;
  },
  &quot;dependencies&quot;: {
    &quot;@apollo/server&quot;: &quot;^4.3.0&quot;,
    &quot;graphql&quot;: &quot;^16.6.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@types/node&quot;: &quot;^18.11.18&quot;,
    &quot;ts-node&quot;: &quot;^10.9.1&quot;,
    &quot;typescript&quot;: &quot;^4.9.4&quot;
  }
}
</code></pre><p>Create a <code>src</code> folder with </p><pre><code class="language-javascript">mkdir src
touch src/index.ts</code></pre><p>and paste the following to bootstrap a basic Apollo Server</p><pre><code class="language-javascript">import { ApolloServer } from &quot;@apollo/server&quot;;
import { startStandaloneServer } from &quot;@apollo/server/standalone&quot;;

const typeDefs = `
  type Book {
	id:ID!
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;

const books = [
  {
    id:1,
    title: &quot;The Awakening&quot;,
    author: &quot;Kate Chopin&quot;,
  },
  {
    id:2,
    title: &quot;City of Glass&quot;,
    author: &quot;Paul Auster&quot;,
  },
];

const resolvers = {
  Query: {
    books: () =&gt; books,
  },
};

const server = new ApolloServer({
  typeDefs, // IMPORTANT
  resolvers, // IMPORTANT
});

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});

console.log(`&#x1F680;  Server ready at: ${url}`);
</code></pre><p>On the terminal, run the command <code>yarn start</code> and you will have the following output when you open <code>http:localhost:4000</code> and execute the query to get all books</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2023/01/Capture-d-e-cran-2023-01-08-a--12.51.02-AM.png" class="kg-image" alt="GraphQL  Backend - Schema-first vs Code-first" loading="lazy" width="1623" height="597" srcset="https://blog.foujeupavel.com/content/images/size/w600/2023/01/Capture-d-e-cran-2023-01-08-a--12.51.02-AM.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2023/01/Capture-d-e-cran-2023-01-08-a--12.51.02-AM.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2023/01/Capture-d-e-cran-2023-01-08-a--12.51.02-AM.png 1600w, https://blog.foujeupavel.com/content/images/2023/01/Capture-d-e-cran-2023-01-08-a--12.51.02-AM.png 1623w" sizes="(min-width: 720px) 720px"></figure><p>In this example, there is something very important to mention. The way the Apollo Server is created. We first define the schema using SDL syntax, define the corresponding resolvers, and pass both to the ApolloServer constructor.</p><p>However, the downside to a schema-first approach is that the resolver functions must match the schema exactly. Since developers need to write the schema and resolvers separately, this becomes a challenge for very large APIs with thousands of lines.</p><p>To this day, this approach does work, but it requires a lot of workarounds and tools to help keep the code clean and inconsistencies to a minimum.</p><h2 id="2-code-first">2. Code-first</h2><p>As described previously, the code-first approach focuses on writing resolvers functions and annotations, then a build tool will compile the schema and the SDL based on types and provided annotations and generate a GraphQL schema based on resolvers types. This approach can speed up the development process since there is no need to check each time that the resolvers match any schema. </p><p>To work with the code-first approach, you will need some third-party libraries like <a href="https://typegraphql.com/">tpegraphql</a> or <a href="https://nexusjs.org/">Nexus</a>. Let&apos;s build a GraphQL API from our previous example using <code>TypeGraphQL</code></p><p><strong>1. Create a new project</strong></p><p>Open your favorite terminal and create a folder with</p><pre><code class="language-javascript">mkdir demo-code-first 
cd demo-code-first</code></pre><p> then run the following commands</p><ul><li><code>yarn init --yes</code> to initialize a node.js project using Yarn as a package manager. this will create a <code>package.json</code> and add the following</li></ul><pre><code class="language-javascript"> &quot;scripts&quot;: {
   &quot;compile&quot;: &quot;tsc&quot;,
   &quot;start&quot;: &quot;npm run compile &amp;&amp; nodemon ./src/index.ts&quot;
  }</code></pre><p><strong>2. Install dependencies</strong></p><ul><li><code>yarn add -D typescript ts-node nodemon @types/node</code> to install typescript and corresponding types</li><li> &#xA0;<code>yarn add @apollo/server typedi class-validator reflect-metadata graphql@15.8.0 type-graphql@2.0.0-beta.1</code> which are dependencies required for our use case.</li></ul><p>Note: Since we are working with Apollo server v4, we use <code>type-graphql</code> version <code>2.0.0-beta.1</code> to avoid errors since Apollo Server V4.0 has &quot;Dropped support for versions of the GraphQL library prior to v16.6.0&quot;. You can have a look at the issue here &#x1F449; <a href="https://github.com/MichalLytek/type-graphql/issues/1385">on GitHub</a> for more details</p><p>Our package.json look like this </p><pre><code class="language-json">{
  &quot;name&quot;: &quot;demo-code-first&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;license&quot;: &quot;MIT&quot;,
  &quot;scripts&quot;: {
    &quot;compile&quot;: &quot;tsc&quot;,
    &quot;start&quot;: &quot;yarn compile &amp;&amp; nodemon ./src/index.ts&quot;
  },
  &quot;dependencies&quot;: {
    &quot;@apollo/server&quot;: &quot;^4.3.0&quot;,
    &quot;class-validator&quot;: &quot;^0.14.0&quot;,
    &quot;graphql&quot;: &quot;^16.6.0&quot;,
    &quot;reflect-metadata&quot;: &quot;^0.1.13&quot;,
    &quot;type-graphql&quot;: &quot;2.0.0-beta.1&quot;,
    &quot;typedi&quot;: &quot;^0.10.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@types/node&quot;: &quot;^18.11.18&quot;,
    &quot;ts-node&quot;: &quot;^10.9.1&quot;,
    &quot;typescript&quot;: &quot;^4.9.4&quot;
  }
}
</code></pre><p>Create a <code>src</code> folder with </p><pre><code class="language-javascript">mkdir src
touch src/index.ts</code></pre><p>and paste the following to bootstrap a basic Apollo Server</p><p>We must ensure that it is imported at the top of our entry file (before we use/import <code>type-graphql</code> or our resolvers):</p><pre><code class="language-typescript">import &quot;reflect-metadata&quot;;</code></pre><p><strong> 3.TypeScript configuration</strong></p><p>It&apos;s important to set these options in the <code>tsconfig.json</code> file of our project:</p><pre><code class="language-json">{
  &quot;emitDecoratorMetadata&quot;: true,
  &quot;experimentalDecorators&quot;: true
}
</code></pre><p><code>TypeGraphQL</code> is designed to work with Node.js LTS (10.3+, 12+) and the latest stable releases. It uses features from ES2018 so we should set our <code>tsconfig.json</code> file appropriately:</p><pre><code class="language-js">{
  &quot;target&quot;: &quot;es2018&quot; // or newer if your node.js version supports this
}
</code></pre><p>Due to using the <code>graphql-subscription</code> dependency that relies on an <code>AsyncIterator</code>, we may also have to provide the <code>esnext.asynciterable</code> to the <code>lib</code> option:</p><pre><code class="language-json">{
  &quot;lib&quot;: [&quot;es2018&quot;, &quot;esnext.asynciterable&quot;]
}
</code></pre><p>All in all, the minimal <code>tsconfig.json</code> file example looks like this:</p><pre><code class="language-json">{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es2018&quot;,
    &quot;module&quot;: &quot;commonjs&quot;,
    &quot;outDir&quot;: &quot;dist&quot;,
    &quot;esModuleInterop&quot;: true,
    &quot;lib&quot;: [&quot;es2018&quot;, &quot;esnext.asynciterable&quot;],
    &quot;experimentalDecorators&quot;: true,
    &quot;emitDecoratorMetadata&quot;: true
  }
}</code></pre><p><strong>4.TypeScript configuration</strong></p><p>It&apos;s important to set these options in the <code>tsconfig.json</code> file of our project:</p><pre><code class="language-json">{
  &quot;emitDecoratorMetadata&quot;: true,
  &quot;experimentalDecorators&quot;: true
}
</code></pre><p><code>TypeGraphQL</code> is designed to work with Node.js LTS (10.3+, 12+) and the latest stable releases. It uses features from ES2018 so we should set our <code>tsconfig.json</code> file appropriately:</p><pre><code class="language-js">{
  &quot;target&quot;: &quot;es2018&quot; // or newer if your node.js version supports this
}
</code></pre><p>Due to using the <code>graphql-subscription</code> dependency that relies on an <code>AsyncIterator</code>, we may also have to provide the <code>esnext.asynciterable</code> to the <code>lib</code> option:</p><pre><code class="language-json">{
  &quot;lib&quot;: [&quot;es2018&quot;, &quot;esnext.asynciterable&quot;]
}
</code></pre><p>All in all, the minimal <code>tsconfig.json</code> file example looks like this:</p><pre><code class="language-json">{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es2018&quot;,
    &quot;module&quot;: &quot;commonjs&quot;,
    &quot;lib&quot;: [&quot;es2018&quot;, &quot;esnext.asynciterable&quot;],
    &quot;experimentalDecorators&quot;: true,
    &quot;emitDecoratorMetadata&quot;: true
  }
}</code></pre><p>Let&apos;s create our server. First, create the following files inside <code>src</code>folder </p><ul><li><code>book.ts</code> </li></ul><pre><code class="language-typescript">import { Field, ID, ObjectType } from &quot;type-graphql&quot;;

@ObjectType()
export class Book {
  @Field((type) =&gt; ID)
  id: number;

  @Field()
  title: string;

  @Field()
  author: string;

  constructor(id: number, title: string, author: string) {
    this.title = title;
    this.author = author;
    this.id = id;
  }
}
</code></pre><ul><li><code>bookResolver.ts</code></li></ul><pre><code class="language-typescript">import { Arg, Query, Resolver } from &quot;type-graphql&quot;;
import { Book } from &quot;./book&quot;;
import { BookService } from &quot;./bookService&quot;;
import { Container } from &quot;typedi&quot;;

@Resolver((of) =&gt; Book)
export class BookResolver {
  constructor(private bookService: BookService) {}

  @Query((returns) =&gt; [Book])
  async books() {
    return Container.get(BookService).getBooks();
  }

  @Query((returns) =&gt; Book)
  async book(@Arg(&quot;id&quot;) id: number) {
    return Container.get(BookService).getBook(id);
  }
}
</code></pre><ul><li><code>bookService.ts</code> thas will behave like our layer between resolvers and Database where we fetch data</li></ul><pre><code class="language-typescript">import { Service } from &quot;typedi&quot;;
import { Book } from &quot;./book&quot;;

@Service()
export class BookService {
  books = [
    {
      id: 1,
      title: &quot;The Awakening&quot;,
      author: &quot;Kate Chopin&quot;,
    },
    {
      id: 2,
      title: &quot;City of Glass&quot;,
      author: &quot;Paul Auster&quot;,
    },
  ];
  getBooks = (): Book[] =&gt; {
    return this.books;
  };

  getBook = (id: number): Book =&gt; {
    return this.books.find((book) =&gt; book.id === id);
  };
}
</code></pre><ul><li><code>index.ts</code></li></ul><pre><code class="language-typescript">import &quot;reflect-metadata&quot;;
import { ApolloServer } from &quot;@apollo/server&quot;;
import { startStandaloneServer } from &quot;@apollo/server/standalone&quot;;
import { buildSchema } from &quot;type-graphql&quot;;
import { BookResolver } from &quot;./bookResolver&quot;;
import * as path from &quot;path&quot;;

const bootstrap = async () =&gt; {
  const schema = await buildSchema({
    resolvers: [BookResolver],
    emitSchemaFile: path.resolve(__dirname, &quot;schema.gql&quot;),
  });
  const server = new ApolloServer({ schema });

  const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
  });

  console.log(`&#x1F680;  Server ready at: ${url}`);
};

bootstrap();
</code></pre><p>On the terminal, run the command <code>yarn start</code> and you will have the following output when you open <code>http:localhost:4000</code> and execute the query to get all books</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2023/01/Capture-d-e-cran-2023-01-08-a--2.47.21-AM.png" class="kg-image" alt="GraphQL  Backend - Schema-first vs Code-first" loading="lazy" width="1905" height="497" srcset="https://blog.foujeupavel.com/content/images/size/w600/2023/01/Capture-d-e-cran-2023-01-08-a--2.47.21-AM.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2023/01/Capture-d-e-cran-2023-01-08-a--2.47.21-AM.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2023/01/Capture-d-e-cran-2023-01-08-a--2.47.21-AM.png 1600w, https://blog.foujeupavel.com/content/images/2023/01/Capture-d-e-cran-2023-01-08-a--2.47.21-AM.png 1905w" sizes="(min-width: 720px) 720px"></figure><p>This will output the exact same result as the previous approach using schema first.</p><p>Note. Something important to mention is this line on our <code>index.ts</code> file</p><pre><code>const schema = await buildSchema({
    resolvers: [BookResolver],
    emitSchemaFile: path.resolve(__dirname, &quot;schema.gql&quot;), /// THIS LINE
  });</code></pre><p>With this option, we tell our server to output the schema file at the root of our <code>src</code> folder. If we take a look at the root folder, we will see a &#xA0;<code>schema.gql</code> file which contains the blueprint of our Graphql API. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2023/01/Capture-d-e-cran-2023-01-08-a--2.53.56-AM.png" class="kg-image" alt="GraphQL  Backend - Schema-first vs Code-first" loading="lazy" width="1154" height="408" srcset="https://blog.foujeupavel.com/content/images/size/w600/2023/01/Capture-d-e-cran-2023-01-08-a--2.53.56-AM.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2023/01/Capture-d-e-cran-2023-01-08-a--2.53.56-AM.png 1000w, https://blog.foujeupavel.com/content/images/2023/01/Capture-d-e-cran-2023-01-08-a--2.53.56-AM.png 1154w" sizes="(min-width: 720px) 720px"></figure><h2 id="3-conclusion">3. Conclusion</h2><p>There are always pros and cons to everything. In my opinion, you should choose the one that is good for you and which you are comfortable with. &#xA0;I can also say that the <strong>schema-first</strong> approach is good for people who just started working with graphql since you will understand if it is the ideal way for you to develop Apis and is the most popular way on the market. However, if you find that over time, it becomes difficult to keep up with the complexity of the project, you can learn Code-first. </p><ul><li>The code source for code-first is available <a href="https://github.com/Doha26/fullstack-hebdo-posts/tree/main/07-demo-code-first">here</a> </li><li>The code source for schema-first is available <a href="https://github.com/Doha26/fullstack-hebdo-posts/tree/main/06-demo-schema-first">here</a> </li></ul><p>Thanks for reading &#x1F609;</p><p><em>Follow me on social media to stay up to date with latest posts.</em></p>]]></content:encoded></item><item><title><![CDATA[Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2]]></title><description><![CDATA[<p>In the previous post, we covered the <a href="https://blog.foujeupavel.com/use-ci-cd-with-github-actions-and-amazon-ecr-to-deploy-a-dockerized-nestjs-backend-on-heroku-part-1/">Part 1</a> of this series. We focused on creating a github repository and configured Github Actions to host a docker image of a NestJS backend. In this post, we will see how to update our Github actions workflow to deploy the image to</p>]]></description><link>https://blog.foujeupavel.com/use-ci-cd-with-github-actions-and-amazon-ecr-to-deploy-a-dockerized-nestjs-backend-on-heroku-part-2/</link><guid isPermaLink="false">62d1b446316abd7fd59e8dba</guid><category><![CDATA[APIs]]></category><category><![CDATA[Backend]]></category><category><![CDATA[CI/CD]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Fri, 15 Jul 2022 21:41:04 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1654277041218-84424c78f0ae?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIwfHxnaXRodWJ8ZW58MHx8fHwxNjU3OTA5NDAy&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1654277041218-84424c78f0ae?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIwfHxnaXRodWJ8ZW58MHx8fHwxNjU3OTA5NDAy&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2"><p>In the previous post, we covered the <a href="https://blog.foujeupavel.com/use-ci-cd-with-github-actions-and-amazon-ecr-to-deploy-a-dockerized-nestjs-backend-on-heroku-part-1/">Part 1</a> of this series. We focused on creating a github repository and configured Github Actions to host a docker image of a NestJS backend. In this post, we will see how to update our Github actions workflow to deploy the image to Heroku. </p><h2 id="1-create-heroku-app">1. &#xA0;Create Heroku App </h2><p>As described in the <a href="https://blog.foujeupavel.com/use-ci-cd-with-github-actions-and-amazon-ecr-to-deploy-a-dockerized-nestjs-backend-on-heroku-part-1/">previous post</a>, <strong>Heroku</strong> is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud. We will host our NestJS Backend here. To do this, go to <a href="https://signup.heroku.com/">https://signup.heroku.com/</a> to create a free Heroku account. Once done, you will access the dashboard. </p><p>Click on <code>New -&gt; Create new app</code> </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--21.25.39.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="1510" height="222" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--21.25.39.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--21.25.39.png 1000w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--21.25.39.png 1510w" sizes="(min-width: 720px) 720px"></figure><p>set the app name, then choose the region and then hit the <code>Create app</code> button. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--21.28.06.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="2000" height="943" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--21.28.06.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--21.28.06.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--21.28.06.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--21.28.06.png 2126w" sizes="(min-width: 720px) 720px"></figure><p>Get an HEROKU_API_KEY for Github Actions. On the dashboard, Go to Account settings -&gt; API Key. Then click on Reveal Button at the right to display your API Key. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--21.44.13.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="2000" height="310" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--21.44.13.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--21.44.13.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--21.44.13.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/07/Capture-d-e-cran-2022-07-15-a--21.44.13.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Copy this Key as HEROKU_API_KEY on Github Actions secrets as described on the <a href="https://blog.foujeupavel.com/use-ci-cd-with-github-actions-and-amazon-ecr-to-deploy-a-dockerized-nestjs-backend-on-heroku-part-1/">previous post</a>. You will also need to set HEROKU_APP_NAME (the name of the heroku app) and HEROKU_EMAIL (the email you used to create your heroku account). Our secrets now are the following.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--21.52.15.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="2000" height="781" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--21.52.15.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--21.52.15.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--21.52.15.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/07/Capture-d-e-cran-2022-07-15-a--21.52.15.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Now, lets update the Github Actions workflow. Open <code>.github/workflows/main.yml</code> and update as follow. &#xA0;</p><pre><code class="language-javascript">name: NestJS-Backend:CI

on:
  push:
    branches: [&apos;master&apos;]

jobs:
  build:
    name: &apos;Build Image&apos;
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-west-2

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{secrets.ECR_REPOSITORY}}
          IMAGE_TAG: ${{github.sha}}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Logout to Amazon ECR
        if: always()
        run: docker logout ${{steps.login-ecr.outputs.registry}}

deploy: ## &lt;= ADDED
    if: ${{ github.ref == &apos;refs/heads/master&apos; }}
    needs: [build]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: akhileshns/heroku-deploy@v3.12.12
        with:
          heroku_api_key: ${{secrets.HEROKU_API_KEY}}
          heroku_app_name: ${{secrets.HEROKU_APP_NAME}}
          heroku_email: ${{secrets.HEROKU_EMAIL}}
          remote_branch: master</code></pre><p>Then push it on github. The following will be displayed</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--22.12.57.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="1614" height="539" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--22.12.57.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--22.12.57.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--22.12.57.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--22.12.57.png 1614w" sizes="(min-width: 720px) 720px"></figure><p>and you will see the following at the end of the deploy</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--22.26.12.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="932" height="320" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--22.26.12.png 600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--22.26.12.png 932w" sizes="(min-width: 720px) 720px"></figure><p>Now let&apos;s see what&apos;s happening on Heroku. </p><p>If we click on the logs of the heroku app on the dashboard here &#xA0;</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--22.40.40.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="2000" height="469" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--22.40.40.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--22.40.40.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--22.40.40.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/07/Capture-d-e-cran-2022-07-15-a--22.40.40.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>we can see the following output. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--22.41.21.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="2000" height="680" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--22.41.21.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--22.41.21.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--22.41.21.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/07/Capture-d-e-cran-2022-07-15-a--22.41.21.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>We have an error message <code>Process exited with status 127</code> because of a <code>not found</code> command. This command is the one to start the NestJS app, to fix this issue, we need to update the package.json file and replace the <code>start</code> script from <code>nest start</code> to <code>node dist/main</code> as follow:</p><pre><code class="language-javascript">&quot;scripts&quot;:{
    ..
    &quot;start&quot;:&quot;node dist/main&quot; 
}</code></pre><p>then stage and push this on github. After all the github actions Jobs, we will see the following on the Heroku Logs </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--22.57.01.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="2000" height="887" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--22.57.01.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--22.57.01.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--22.57.01.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/07/Capture-d-e-cran-2022-07-15-a--22.57.01.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Our app is now running properly on Heroku but we still have one issue to fix to be able to Run some http request on our NestJS app. </p><p>if we click on the <code>Open app</code> button at the top, heroku will try to open our app at `<a href="https://nestjs-heroku-docker.herokuapp.com/">https://nestjs-heroku-docker.herokuapp.com/</a>` but we will see the following error </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--23.03.49.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="2000" height="767" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--23.03.49.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--23.03.49.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--23.03.49.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--23.03.49.png 2108w" sizes="(min-width: 720px) 720px"></figure><p>Why do we get this error ? let&apos;s see the logs output</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--23.05.56.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 2" loading="lazy" width="2000" height="357" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--23.05.56.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--23.05.56.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--23.05.56.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/07/Capture-d-e-cran-2022-07-15-a--23.05.56.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>We have this error <code>Error R10 (Boot timeout) -&gt; Web process failed to bind to $PORT within 60 seconds of launch</code>. What does it means ? </p><p>The answer is simple. Each time you deploy on app on Heroku, Heroku will bind your app to a random port and will set this port to environment variable with the name PORT. So the correct way to make your app listen to this port is by using <code>process.env.PORT</code> . Let&apos;s update the <code>main.ts</code> file of our app. </p><pre><code class="language-javascript">..
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT || 3000);
}
bootstrap();</code></pre><p>Now stage everything and push to github. Our app build and deploy success fully. To test it open <a href="https://nestjs-heroku-docker.herokuapp.com/">https://nestjs-heroku-docker.herokuapp.com/</a> and you will see the <code>Hello World !</code> message printed. </p><p> In the next posts we will for sure dive more deeper into NestJS and see how to create a Robust GraphQL backend with. </p><p>Thanks for reading. The source code is available <a href="https://github.com/Doha26/nestjs-docker-heroku">here</a>.</p><p><em>Follow me on socials media to stay up to date with latest posts.</em></p>]]></content:encoded></item><item><title><![CDATA[Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1]]></title><description><![CDATA[<p>Hey guys &#x1F91A;, Glad to have you here! In this post we are going to cover how to configure github actions with docker to deploy a Node.js backend. </p><p>To complete this post, you need to have to have:</p><ul><li>A Github account</li><li>A AWS account </li><li>A Heroku account </li><li>Basics knowledge</li></ul>]]></description><link>https://blog.foujeupavel.com/use-ci-cd-with-github-actions-and-amazon-ecr-to-deploy-a-dockerized-nestjs-backend-on-heroku-part-1/</link><guid isPermaLink="false">62d044b6316abd7fd59e8946</guid><category><![CDATA[APIs]]></category><category><![CDATA[Backend]]></category><category><![CDATA[CI/CD]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Fri, 15 Jul 2022 18:30:46 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1654277041218-84424c78f0ae?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIwfHxnaXRodWJ8ZW58MHx8fHwxNjU3OTA5NDAy&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1654277041218-84424c78f0ae?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIwfHxnaXRodWJ8ZW58MHx8fHwxNjU3OTA5NDAy&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1"><p>Hey guys &#x1F91A;, Glad to have you here! In this post we are going to cover how to configure github actions with docker to deploy a Node.js backend. </p><p>To complete this post, you need to have to have:</p><ul><li>A Github account</li><li>A AWS account </li><li>A Heroku account </li><li>Basics knowledge of Node.js and Javascript</li><li>Basic Understanding of Docker</li></ul><p>We will divide this post in 4 parts. </p><ul><li><a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-1/">Part 1</a> : Create a basic NestJS backend, create and configure Github &#xA0;Actions and Amazon ECR to deploy a docker image of our NestJS backend</li><li><a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-2/">Part 2</a> : Update the Github Actions workflow to pull image from Amazon ECR and deploy it to Heroku</li></ul><h2 id="1-our-tools">1. Our tools</h2><p>Some key concepts we are going to cover here in this post: </p><ul><li><strong>CI/CD</strong>: those two expressions stands for Continuous integration (CI) and continuous delivery (CD). They represents a coding philosophy and set of practices that drive development teams to frequently implement and deliver small code changes and check them in to a version control repository. </li><li><strong><a href="https://github.com/features/actions">Github Actions</a></strong>: according to <a href="https://resources.github.com/">https://resources.github.com/</a>, GitHub Actions gives developers the ability to automate their workflows across issues, pull requests, and more&#x2014;plus native CI/CD functionality</li><li><a href="https://www.heroku.com/"><strong>Heroku</strong>:</a> Heroku is a cloud application platform that makes it easy to both develop and deploy web applications in a variety of languages.</li><li><strong><a href="https://www.docker.com/">Docker</a></strong> : is an open source platform for building, deploying, and managing containerized applications.</li><li><a href="https://nestjs.com/"><strong>NestJS</strong></a> : is a progressive Node.js framework for building efficient, reliable and scalable server-side applications</li><li><strong>Amazon ECR</strong>: Amazon Elastic Container Registry (Amazon ECR) is an Amazon Web Service (<a href="https://www.techtarget.com/searchaws/definition/Amazon-Web-Services">AWS</a>) product that stores, manages and deploys <a href="https://www.techtarget.com/searchitoperations/definition/Docker-image">Docker images</a>, which are managed clusters of <a href="https://www.techtarget.com/searchaws/definition/Amazon-EC2-instances">Amazon EC2 instances</a></li></ul><p>With this brief introduction our the main tools we are going to use, let&apos;s start. </p><h2 id="1-create-a-nestjs-application">1. Create a NestJS Application</h2><p>to start working with the Nestjs framework, we need to install the Nestjs CLI. Open your favorite terminal and run the following command</p><pre><code>npm install -g @nestjs/cli</code></pre><p>This will install NestJS cli globally. Now we can creaate a new NestJS project. run the following command on the terminal </p><pre><code>nest new nestjs-docker-heroku</code></pre><p>The <code>new</code> command initialise a new nestjs project with the given name. After running this command, nest will ask you wich package manager you want to use. You can choose <code>npm</code> or <code>yarn</code> i&apos;m going to use <code>Yarn</code> . If you have an error while installing depencies with yarn, run the following command <code>npm i -g yarn</code> and then retry the <code>nest new xxx</code> command. </p><p>At the end o the install process, you will see the following message</p><pre><code>&#x1F680;  Successfully created project nestjs-docker-heroku
&#x1F449;  Get started with the following commands:

$ cd nestjs-docker-heroku
$ yarn run start</code></pre><p>The basic setup of our nestJS project expose a unique endpoint <code>/</code> which display an <code>Hello World!</code> message at <code>http://localhost:3000</code> </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-14-a--23.20.14.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="2000" height="665" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-14-a--23.20.14.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-14-a--23.20.14.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-14-a--23.20.14.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/07/Capture-d-e-cran-2022-07-14-a--23.20.14.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Now we have a basic NestJS backend. Let&apos;s &#xA0;consider our backend only expose this basic endpoint. Let&apos;s create a github repository. </p><h2 id="2-prepare-a-github-repo">2. Prepare a Github Repo</h2><p>Create a git repository <a href="https://github.com/new">here</a> &#xA0;and link to the newly created project. If you don&apos;t have a github account, sign up <a href="https://github.com/join">here</a>.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-14-a--19.49.53.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="946" height="460" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-14-a--19.49.53.png 600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-14-a--19.49.53.png 946w" sizes="(min-width: 720px) 720px"></figure><p>Go to github and get the repository url. We will set this url as the github url origin of our newly created nestJS project. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-14-a--20.33.34.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="2000" height="1352" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-14-a--20.33.34.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-14-a--20.33.34.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-14-a--20.33.34.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-14-a--20.33.34.png 2000w" sizes="(min-width: 720px) 720px"></figure><p>Copy the repository url and go to the root of your project to run the following.</p><pre><code>git remote add origin https://github.com/Doha26/nestjs-docker-heroku.git</code></pre><p>Add all our project files to version control and push it to the github repo. To do this, run the following: </p><pre><code>git add . // To stage all files
</code></pre><pre><code>git commit -am &quot;initial setup&quot; // commit our files</code></pre><pre><code>git push -u origin master // Push everything to github</code></pre><h2 id="3-create-a-docker-file-for-the-nestjs-app">3. &#xA0;Create a Docker file for the NestJS app</h2><p>Now that we have our Back-End API app up and running, let&apos;s containerize it using Docker.</p><p>Start by creating a Dockerfile named <code>Dockerfile</code> file in the project&apos;s root directory with the following content: </p><pre><code class="language-javascript">FROM node:lts-alpine as build

WORKDIR /usr/src/app

COPY package.json yarn.lock ./

RUN yarn 

COPY . .

# Build
RUN yarn build

### Build production image

FROM node:lts-alpine as prod

WORKDIR /usr/src/app

COPY --from=build /usr/src/app/dist ./dist
COPY --from=build /usr/src/app/package.json ./
COPY --from=build /usr/src/app/yarn.lock ./

EXPOSE 3000

RUN yarn install --frozen-lockfile --production

CMD [&quot;node&quot;, &quot;dist/main&quot;]</code></pre><p>In this single Docker file, we create an image from existing Node image <code>node:lts-alpine</code> that will be pulled from docker hub. </p><h3 id="test-the-docker-image-locally">Test the docker image locally </h3><p>We can build and run our docker container locally to see if it works as expected. Open the command line and run the following at the root of the project.</p><pre><code class="language-javascript">docker build -t nestjs-docker-heroku .</code></pre><p>Once this complete successfully, run it in detached mode with the following command and bind the port 3000 of our container to port 4000 on our localhost</p><pre><code>docker run -d -p4000:3000 nestjs-docker-heroku</code></pre><p>If you open you go to <code>http://localhost:4000</code> &#xA0;you will see the message <code>Hello World!</code> &#xA0;that is displayed when you access the <code>/</code>route. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--20.02.45.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="1724" height="792" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--20.02.45.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--20.02.45.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--20.02.45.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--20.02.45.png 1724w" sizes="(min-width: 720px) 720px"></figure><p>With &#xA0;all this set, let&apos;s &#xA0;go to the next step.</p><h2 id="5-configuring-amazon-ecr">5. &#xA0;Configuring Amazon ECR</h2><p>Go to <a href="https://aws.amazon.com/fr/console/">AWS Management console</a> and login. Once connected, search for <strong>ECR </strong>in the search area and then choose <code>Elastic Container Registry</code>. On the next screen choose private and then set a repository name in the given input as follow</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--13.35.43-1.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="971" height="589" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--13.35.43-1.png 600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--13.35.43-1.png 971w" sizes="(min-width: 720px) 720px"></figure><p>Note &#x261D;&#xFE0F; : We used the same name <code>nestjs-docker-heroku</code> that we use for <code>ECR_REPOSITORY</code> in our workflow file <code>main.yml</code> </p><p>Once created, we have our repository created </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--13.36.06-1.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="1479" height="466" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--13.36.06-1.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--13.36.06-1.png 1000w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--13.36.06-1.png 1479w" sizes="(min-width: 720px) 720px"></figure><h3 id="aws-region">AWS Region</h3><p>An important point to note is the AWS region where the repository is hosted. if we take a look at the repository <code>URI</code> we have the following. <code>xxxxxxxx.dkr.ecr.</code><strong><code>us-west-2</code></strong><code>.amazonaws.com/nestjs-docker-heroku</code>. In this URI the AWS Region is <code><strong>us-west-2</strong></code><strong>. </strong>You<strong> </strong>can see it on your management console next to your Account name on the top menu bar. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--19.45.36.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="1372" height="303" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--19.45.36.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--19.45.36.png 1000w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--19.45.36.png 1372w" sizes="(min-width: 720px) 720px"></figure><p>We will use this same region in our Github Actions workflow file. </p><p>Since we have to deploy the docker image to ECR. So, we have to reference some AWS secrets in the GitHub Actions workflow. These secrets are &#xA0;AWS_ACCESS_KEY_ID<strong> </strong>and<strong> </strong>AWS_SECRET_ACCESS_KEY<strong> &#xA0;</strong>and they should be present in &#x201C;repository secrets&#x201D;. To do so, go to your AWS Account, click on your name at the top right then choose <code>Security credentials</code>. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--14.23.12-1.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="672" height="429" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--14.23.12-1.png 600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--14.23.12-1.png 672w"></figure><p>On the screen that appear, click on <code>Access keys (access key ID and secret access key)</code> dropdown and click on <code>Create new Access key</code> , a popup will open and here, click on <code>Show Access key</code> and you will see both keys.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--14.36.45.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="2000" height="741" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--14.36.45.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--14.36.45.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--14.36.45.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--14.36.45.png 2104w" sizes="(min-width: 720px) 720px"></figure><h2 id="5-configuring-github-actions">5. &#xA0;Configuring Github Actions</h2><p>Github actions as described previously is a tool that will provide us with configurable workflow that will host <code>Jobs</code> that will be executed concurrently as well as sequentially &#xA0;every time we push some code changes on the master branch of our repository. A job A job is a set of <em>steps</em> in a workflow that execute on the same runner. Each step is either a shell script that will be executed, or an <em>action</em> that will be run. For our use case, our jobs will do the following : </p><ul><li>Use the Dockerfile of our NestJS app to build a Docker image and push it to <strong>AWS ECR</strong>, An Amazon Container Registry. </li><li>Pull the image from AWS ECR to deploy it to Heroku</li></ul><h3 id="workflow-in-github-actions">Workflow in GitHub Actions</h3><ul><li>A Workflow is a process that consists of one or multiple jobs.</li><li>Workflow gets triggered by an <strong><strong>event</strong></strong> or manually.</li><li>An event is a specific activity in a repository that triggers a workflow run. E.g- When someone creates a pull request or pushes a commit to a repository.</li><li>Workflows use a YAML syntax and are present in the <strong><strong>.github/workflows directory</strong></strong> in your repository.</li></ul><h3 id="creating-a-workflow">Creating a Workflow</h3><p>Let&#x2019;s create a workflow to deploy the docker image to the ECR repository. Go to your Github repository and click on the <code>Actions</code> menu. In the &#xA0;page that appear, click on <code><a href="https://github.com/Doha26/nestjs-docker-heroku/new/master?filename=.github%2Fworkflows%2Fmain.yml&amp;workflow_template=blank">set up a workflow yourself</a> -&gt;</code> </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--00.20.56.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="964" height="355" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--00.20.56.png 600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--00.20.56.png 964w" sizes="(min-width: 720px) 720px"></figure><p>You will see the following</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--00.51.10.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="1922" height="519" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--00.51.10.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--00.51.10.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--00.51.10.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--00.51.10.png 1922w" sizes="(min-width: 720px) 720px"></figure><p>Keep the default settings and click <code>start commit</code> to commit the file with the default <code>main.yml</code> name. The workfow file is not added to your repository. With this changes on the remote repository, we need to sync it with your local repository. Make a <code>git pull</code> to fetch remotes changes on your local repository. Now on your project root folder your will be able to see a <code>.github</code> folder with a <code>main.yml</code> inside. We are going to update this <code>main.yml</code> with the following </p><pre><code class="language-javascript">name: NestJS-Backend:CI

on:
  push:
    branches: [&apos;master&apos;]

jobs:
  build:
    name: &apos;Build Image&apos;
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-west-2

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Build, tag, and push image to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{secrets.ECR_REPOSITORY}}
          IMAGE_TAG: ${{github.sha}}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Logout to Amazon ECR
        if: always()
        run: docker logout ${{steps.login-ecr.outputs.registry}}
</code></pre><p>Let&apos;s explain the content of this file.</p><ul><li><code>name</code> &#xA0;This is just the name of our Action</li><li><code>on push branches master</code> the action will be triggered each time with push on branch master or when a pull request is created to be merged on master</li><li><code>env</code> Some environment variable the workflow will use to run jobs.</li><li><code>jobs | build | runs-on</code> this tell the workflow to create a Ubuntu remote environment to run and build the image </li><li><code>Chekout</code> Log in to the Remote Machine via SSH using the pre-written workflow by Official GitHub Actions i.e <a href="https://github.com/actions/checkout" rel="noopener ugc nofollow">Checkout</a>. It simply checks out our GitHub repository for <code>Dockerfile</code> to build the docker image</li><li><code>Configure AWS Credentials</code> Setting up AWS CLI/SDK on Remote Host and Configuring AWS Login Credentials and Assuming Roles using the pre-written workflow by Official AWS Teams i.e <a href="https://github.com/aws-actions/configure-aws-credentials" rel="noopener ugc nofollow">Configure AWS Credentials</a>. For accessing the AWS ECR we need to define a custom Role in later steps.</li><li><code>Login to Amazon ECR</code> Use AWS/CLI Sdk to login to Amazon ECR </li><li><code>Build, tag, and push image to Amazon ECR</code> Building the Docker Image by &#xA0;using the Dockerfile at the root of the repository, Tagging the Image with a version, and Pushing it to an Elastic Container Registry (Private ECR)</li><li><code>Logout to Amazon ECR</code> this logout to Amazon ECR once the image is pushed successfully</li></ul><p>To Understand the GitHub Actions syntax please refer to <a href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions" rel="noopener ugc nofollow">this </a>link.With all this set, let&apos;s go to AWS Management console to create an AWS ECR repository. </p><h3 id="setting-aws-secrets">Setting AWS Secrets</h3><p>In the previous section , we have seen how to get AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, now we need to provide this key for our Github Actions workflow. Go to Github <strong><strong>Settings</strong></strong>, then <strong><strong>Secrets</strong></strong>, and then <strong><strong>New repository secret</strong></strong>.</p><ul><li>AWS_ACCESS_KEY_ID &#x2014; The AWS Access Key ID</li><li>AWS_SECRET_ACCESS_KEY &#x2014; The AWS Secret Access Key</li><li>ECR_REPOSITORY &#x2014; The name of the AWS ECR repository you created</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--14.45.21.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="1261" height="292" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--14.45.21.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--14.45.21.png 1000w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--14.45.21.png 1261w" sizes="(min-width: 720px) 720px"></figure><p>Once we have all this set, let&apos;s push our local changes including <code>Dockerfile</code>, <code>main.yml</code> and <code>.dockerignore</code> on our repository to trigger Github actions jobs. Stage all changes and push to github. You will see a new Workflow in Github Actions tab. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--16.06.15.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="1732" height="439" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--16.06.15.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--16.06.15.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--16.06.15.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--16.06.15.png 1732w" sizes="(min-width: 720px) 720px"></figure><p>And if you click on the workflow, you will see workflow jobs that are running</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--19.55.57.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="1917" height="999" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--19.55.57.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--19.55.57.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/07/Capture-d-e-cran-2022-07-15-a--19.55.57.png 1600w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--19.55.57.png 1917w" sizes="(min-width: 720px) 720px"></figure><p>After everything complete, you will see the following output</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--19.57.29.png" class="kg-image" alt="Use CI/CD with Github Actions and Amazon ECR to deploy a Dockerized NestJS Backend on Heroku - Part 1" loading="lazy" width="1138" height="584" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/07/Capture-d-e-cran-2022-07-15-a--19.57.29.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/07/Capture-d-e-cran-2022-07-15-a--19.57.29.png 1000w, https://blog.foujeupavel.com/content/images/2022/07/Capture-d-e-cran-2022-07-15-a--19.57.29.png 1138w" sizes="(min-width: 720px) 720px"></figure><p>Great! Our docker image is now hosted on AWS ECR. That&apos;s the end of this first part, in the part, we will see how to update our workflow file to pull and deploy this image on Heroku. </p><p>Thanks for reading. The source code is available <a href="https://github.com/Doha26/nestjs-docker-heroku">here</a></p><p><em>Follow me on socials media to stay up to date with latest posts.</em></p>]]></content:encoded></item><item><title><![CDATA[Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 3]]></title><description><![CDATA[<p>Hey guys &#x1F91A;, Glad to have you here.</p><p>In the previous post, we covered the <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-2/">Part </a>2 of this series about building a complete Fullstack app called <a href="https://dribbble.com/shots/14700802-News-Section/attachments/6399696?mode=media">FoxNews</a> &#xA0;using Amazon Web Services. <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-1/">The Part 1</a> was focused on setting our tools and host the basic setup of our Next.</p>]]></description><link>https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-3/</link><guid isPermaLink="false">62ae49d0a72f86064975caea</guid><category><![CDATA[Front-end]]></category><category><![CDATA[Typescript]]></category><category><![CDATA[Backend]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Graphql]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Tue, 28 Jun 2022 20:31:40 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1638913970895-d3df59be1466?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wxfDF8YWxsfDF8fHx8fHwyfHwxNjU1NTAzMDY2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1638913970895-d3df59be1466?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wxfDF8YWxsfDF8fHx8fHwyfHwxNjU1NTAzMDY2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 3"><p>Hey guys &#x1F91A;, Glad to have you here.</p><p>In the previous post, we covered the <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-2/">Part </a>2 of this series about building a complete Fullstack app called <a href="https://dribbble.com/shots/14700802-News-Section/attachments/6399696?mode=media">FoxNews</a> &#xA0;using Amazon Web Services. <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-1/">The Part 1</a> was focused on setting our tools and host the basic setup of our Next.js app on Amazon Amplify using Amplify Framework. </p><p>In this post, we will be able to fetch and displays news from remote API called <a href="https://newscatcherapi.com/">NewsCatcherAPI</a>. Since we have a limited plan (Trial Plan with 1000 request) at <a href="https://newscatcherapi.com/">https://newscatcherapi.com/</a>, &#xA0;we don&apos;t want to exhaust our plan. That&apos;s why we are going to &#xA0;to fetch news once when the user open the app and then store the result in a kind of local database for all future actions when user brows the app. to do so, we are going to use <a href="https://redux.js.org/">Redux</a> with <a href="https://redux-toolkit.js.org/">Redux toolkit</a>. The main purpose of this part will be :</p><ul><li>setup redux with redux toolkit</li><li>deploy the result on Amazon Amplify</li></ul><h2 id="1-put-everything-in-place">1. Put everything in place </h2><p>As we are using existing project, we just need to clone the project at the stage we left in our <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-2/">previous post</a> where we hosted the basic version of our Next.js App with the main and the detail page. To do so, we need to run this in the terminal</p><pre><code class="language-javascript">git clone -b feat/part2-ui-layer --single-branch https://github.com/Doha26/Foxnews-nextjs-aws.git</code></pre><p>With this, you fetch all the branches in the repository, checkout to the one you specified, and the specific branch becomes the configured local branch for <code>git push</code> and <code>git pull</code> The <code>--single-branch</code> flag (introduced in Git version 1.7.10) allows you to only fetch files from the specified branch without fetching other branches</p><p>Go tot the root of the project with <code>cd foxnews-nextjs-aws and yarn</code></p><p>Create a new branch to track all the changes we will make during this Part 2. To do so, run this inside the terminal.</p><ul><li><code>git checkout -b feat/part3-redux</code> to create a new branch called <code>feat/part3-redux</code></li><li><code>git push --set-upstream origin feat/part3-redux</code> push everything on github to start</li><li>run yarn dev to serve the default setup at <code>localhost:3000</code> that looks like this</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-25-a--23.03.35.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 3" loading="lazy" width="2000" height="1267" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-25-a--23.03.35.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-25-a--23.03.35.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-25-a--23.03.35.png 1600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-25-a--23.03.35.png 2216w" sizes="(min-width: 720px) 720px"></figure><p>Well &#x1F44F;, if you have this, it&apos;s means we are ready for the next step. Let&apos;s go &#x1F3C4;&#x1F3FC;&#x200D;&#x2642;&#xFE0F;</p><p>In the <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-2/">previous post</a>, we hardcoded the displayed news in the code in the <code>src/utils</code> file. In this post, we are going to refacto this and fetch news from the remote newscacther API. Let&apos;s start.</p><h2 id="2-get-an-api-key">2. Get an API Key</h2><p>Go to newsCatcherAPI website <a href="https://newscatcherapi.com/">here</a> and on the top menu, got to <strong>Get API Key. </strong></p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-25-a--23.14.30.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 3" loading="lazy" width="1339" height="147" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-25-a--23.14.30.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-25-a--23.14.30.png 1000w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-25-a--23.14.30.png 1339w" sizes="(min-width: 720px) 720px"></figure><p>you will be asked to register and choose a plan. You can choose the <strong>free plan</strong> that is absolutely good for our use case. Once Done, you will have access to the dashboard and here you will se your API key displayed. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-25-a--23.22.37.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 3" loading="lazy" width="2000" height="928" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-25-a--23.22.37.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-25-a--23.22.37.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-25-a--23.22.37.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-25-a--23.22.37.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>now you have an API Key, create a file named <code>.env.local</code> that will host environment variables. Next.js comes with built-in support for environment variables, which allows you to do the following:</p><ul><li>Use <code>.env.local</code> to load environment variables</li><li>Expose environment variables to the browser by prefixing with <code>NEXT_PUBLIC_</code></li></ul><p>So inside <code>.env.local</code> add the following</p><pre><code>NEXT_PUBLIC_NEWS_API_KEY=XXXXX_YOUR_API_KEY_XXXXXX</code></pre><p>This, done, make sure this file is not tracked by git. check the <code>.gitignore</code> and if not present, add it manually.</p><h2 id="2-fetch-news-with-your-api-key">2. Fetch News with your API Key</h2><p>we are going to create a utils function to fetch news using the API Key. Inside the <code>lib</code> folder at the root of the project, create a file named <code>getNews.ts</code> and add the following:</p><pre><code class="language-javascript">const getNews = async (url: string) =&gt; {
  const response = await fetch(url, {
    method: &apos;GET&apos;,
    headers: {
      &apos;Content-Type&apos;: &apos;aplication/json&apos;,
      &apos;x-api-key&apos;: process.env.NEXT_PUBLIC_NEWS_API_KEY || &apos;&apos;
    }
  })
  const data = await response.json()
  return data
}

export default getNews
</code></pre><p>Note the line 6 with this <code>&apos;x-api-key&apos;: process.env.NEXT_PUBLIC_NEWS_API_KEY || &apos;&apos;</code>. This line set an Http header with the Newscatcher API Key. Without this key, you will not be able to get results from the remote API. </p><h2 id="2-setup-redux-and-redux-toolkit">2. Setup Redux and Redux toolkit</h2><p>before we start explaining how to setup Redux, we will take some lines to explain what is behind the tool and when it can be useful to use.</p><h3 id="what-is-redux">What is Redux ? </h3><p><a href="https://redux.js.org/">Redux</a> is a predictable state container for JavaScript apps. As the application grows, it becomes difficult to keep it organized and maintain data flow. Redux solves this problem by managing application&#x2019;s state with a single global object called Store. Redux fundamental principles help in maintaining consistency throughout your application, which makes debugging and testing easier.</p><h3 id="why-should-i-use-redux%E2%80%8B">Why Should I Use Redux?<a href="https://redux.js.org/tutorials/fundamentals/part-1-overview#why-should-i-use-redux">&#x200B;</a></h3><p>Redux helps you manage &quot;global&quot; state - state that is needed across many parts of your application.</p><p><strong>The patterns and tools provided by Redux make it easier to understand when, where, why, and how the state in your application is being updated, and how your application logic will behave when those changes occur</strong>. Redux guides you towards writing code that is predictable and testable, which helps give you confidence that your application will work as expected.</p><h3 id="when-should-i-use-redux%E2%80%8B">When Should I Use Redux?<a href="https://redux.js.org/tutorials/fundamentals/part-1-overview#when-should-i-use-redux">&#x200B;</a></h3><p>Redux helps you deal with shared state management, but like any tool, it has tradeoffs. There are more concepts to learn, and more code to write. It also adds some indirection to your code, and asks you to follow certain restrictions. It&apos;s a trade-off between short term and long term productivity.</p><p>Redux is more useful when:</p><ul><li>You have large amounts of application state that are needed in many places in the app</li><li>The app state is updated frequently over time</li><li>The logic to update that state may be complex</li><li>The app has a medium or large-sized codebase, and might be worked on by many people</li></ul><p>The following image from redux documentation let you understand the redux flow</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/reduxdataflowdiagram-49fa8c3968371d9ef6f2a1486bd40a26.gif" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 3" loading="lazy" width="1440" height="1080"></figure><p>You can read more at the Redux documentation here <a href="https://redux.js.org/tutorials/fundamentals/part-1-overview">https://redux.js.org/tutorials/fundamentals/part-1-overview</a></p><h3 id="redux-toolkit">Redux toolkit</h3><p>According to the Redux team, Redux Toolkit was created to address <a href="https://redux.js.org/redux-toolkit/overview" rel="nofollow">three major concerns</a>:</p><ol><li>&quot;Configuring a Redux store is too complicated&quot;</li><li>&quot;I have to add a lot of packages to get Redux to do anything useful&quot;</li><li>&quot;Redux requires too much boilerplate code&quot;</li></ol><p>Simply, <strong>Redux Toolkit provides us with tools that help abstract over the setup process that used to be troublesome. </strong></p><p>Some of &#xA0;functions that simplify the different parts of Redux are : </p><ul><li><strong>configureStore</strong> : to simplify store creation</li><li><strong>createAction</strong> : to create actions easily </li><li><strong>createReducer </strong>: to simplify reducers creation</li></ul><p>ReduxToolkit also provide us with another way to combine <code>createAction</code> and <code>createReducer</code> in a single call using <code>Slice</code> with <code>createSlice()</code> function. Let&apos;s see all this in action. to start, we need to install required packages. Open your favorite terminal and run the followings.</p><pre><code>yarn add @reduxjs/toolkit next-redux-wrapper react-redux redux-persist </code></pre><p>create a slice file under <code>src/stores/slice/articleSlice.ts</code></p><pre><code class="language-javascript">import { createAsyncThunk, createSlice, Draft, PayloadAction } from &apos;@reduxjs/toolkit&apos;
import type { RootState } from &apos;../store&apos;
import { NewsAPIObject, NEWS_API_URL } from &apos;../../utils&apos;
import { HYDRATE } from &apos;next-redux-wrapper&apos;
import getNews from &apos;../../../lib/getNews&apos;

// declaring the types for our state
export type NewsState = {
  articles: NewsAPIObject[]
  pending: boolean
  error: boolean
  fetched: boolean
  error_code: string
}

const initialState: NewsState = {
  articles: [],
  pending: false,
  error: false,
  fetched: false,
  error_code: &apos;&apos;
}

// Get NEWS from NewsCatcherAPI
export const getArticles = createAsyncThunk(&apos;getAticles&apos;, async (_, { rejectWithValue }) =&gt; {
  const { status, articles, error_code } = await getNews(`${NEWS_API_URL}News`)
  return status === &apos;ok&apos; ? { error: null, articles } : rejectWithValue({ articles: [], error_code })
})

// HeadersWithoutKey
export const articlesSlice = createSlice({
  name: &apos;news&apos;,
  initialState,
  reducers: {
    setArticles: (state: Draft&lt;typeof initialState&gt;, action: PayloadAction&lt;NewsAPIObject[]&gt;) =&gt; {
      state.articles = action.payload
    }
  },
  extraReducers: builder =&gt; {
    builder
      .addCase(HYDRATE, (_state: Draft&lt;typeof initialState&gt;) =&gt; {
        console.log(&apos;HYDRATE&apos;, _state)
      })
      .addCase(getArticles.fulfilled, (state, { payload }) =&gt; {
        console.log(&apos;FULFILLED&apos;)
        state.pending = false
        state.articles = payload.articles
        state.fetched = true
      })
      .addCase(getArticles.pending, state =&gt; {
        console.log(&apos;PENDING&apos;)
        state.pending = true
        state.fetched = false
      })
      .addCase(getArticles.rejected, (state, { payload }) =&gt; {
        console.log(&apos;rejected&apos;)
        state.pending = false
        state.error = true
        state.fetched = false
        //@ts-ignore
        state.error_code = payload.error_code
      })
  }
})
// Here we are just exporting the actions from this slice, so that we can call them anywhere in our app.
export const { setArticles } = articlesSlice.actions

// calling the above actions would be useless if we could not access the data in the state. So, we use something called a selector which allows us to select a value from the state.
export const selectArticles = (state: RootState) =&gt; state?.news || initialState

// exporting the reducer here, as we need to add this to the store
export default articlesSlice.reducer
</code></pre><p>With all this installed, let&apos;s create a redux store. Under <code>src/store/store.ts</code> </p><pre><code class="language-javascript">import { Action, combineReducers, configureStore, createStore, ThunkAction, applyMiddleware } from &apos;@reduxjs/toolkit&apos;;
import { createWrapper } from &apos;next-redux-wrapper&apos;
import storage from &apos;redux-persist/lib/storage&apos;
import { persistReducer, persistStore } from &apos;redux-persist&apos;
import newsReducer from &apos;./slice/articleSlice&apos;

//@ts-ignore
const makeConfiguredStore = reducer =&gt; createStore(reducer, undefined, applyMiddleware())

const reducers = combineReducers({
  news: newsReducer
})

const persistConfig = {
  key: &apos;foxnews&apos;,
  whitelist: [&apos;news&apos;],
  storage
}
const persistedReducer = persistReducer(persistConfig, reducers)

const makeStore = () =&gt; {
  let store
  const isServer = typeof window === &apos;undefined&apos;
  if (isServer) {
    store = makeConfiguredStore(newsReducer)
  } else {
    store = configureStore({
      reducer: persistedReducer,
      middleware: getDefaultMiddleware =&gt;
        getDefaultMiddleware({
          serializableCheck: false
        }),
      devTools: true
    })
    //@ts-ignore
    store.__persistor = persistStore(store)
  }
  return store
}

export type AppDispatch = ReturnType&lt;typeof makeStore&gt;[&apos;dispatch&apos;]
export type AppStore = ReturnType&lt;typeof makeStore&gt;
export type RootState = ReturnType&lt;AppStore[&apos;getState&apos;]&gt;
export type AppThunk&lt;ReturnType = void&gt; = ThunkAction&lt;ReturnType, RootState, unknown, Action&lt;string&gt;&gt;
export const wrapper = createWrapper&lt;AppStore&gt;(makeStore, { debug: true })
</code></pre><p>NB: You can see that we create a persistable store using <code>redux-persist</code>. This way, we will fetch data once and then persist to the localStorage and prevent our App to fetch everytime the user refresh the browser. </p><p>Now we have a Slice and the store, let&apos;s create our customs Hooks that override the default behavior of two Redux function. <code>useDispatch()</code> and <code>useSelector()</code> </p><p>under <code>src/store</code> create a file named <code>hooks.ts</code> with the following </p><pre><code class="language-javascript">import { TypedUseSelectorHook, useDispatch, useSelector } from &apos;react-redux&apos;
import type { AppDispatch, RootState } from &apos;./store&apos;

export const useAppDispatch = () =&gt; useDispatch&lt;AppDispatch&gt;()
export const useAppSelector: TypedUseSelectorHook&lt;RootState&gt; = useSelector
</code></pre><p>With all this set, we now just have to wrap our main <code>_app.tsx</code> component logic inside a component provided by redux-persist and provide the component with our created store. Open <code>_app.tsx</code> at the root and add the following :</p><pre><code class="language-javascript">import &apos;../../styles/globals.css&apos;
import type { AppProps } from &apos;next/app&apos;
import { wrapper } from &apos;../store/store&apos;
import { PersistGate } from &apos;redux-persist/integration/react&apos;
import { useStore } from &apos;react-redux&apos;
function MyApp({ Component, pageProps }: AppProps) {
  const store = useStore()

  return (
    /* 
    // @ts-ignore */
    &lt;PersistGate persistor={store.__persistor} loading={null}&gt;
      &lt;Component {...pageProps} /&gt;
    &lt;/PersistGate&gt;
  )
}

export default wrapper.withRedux(MyApp)
</code></pre><p>With this set, we are good to go with redux. let&apos;s update our pages components to see it in action. </p><p>Update <code>src/pages/index.tsx</code> and add some logic. </p><pre><code class="language-javascript">...
import { useAppDispatch, useAppSelector } from &apos;../store/hooks&apos;
import { getArticles, selectArticles } from &apos;../store/slice/articleSlice&apos;
...

const Home: NextPage = () =&gt; {
    const dispatch = useAppDispatch()
  const { articles, error, error_code } = useAppSelector(selectArticles)
  .. 
  
  useEffect(() =&gt; {
    //@ts-ignore
    dispatch(getArticles())
    setMessageTitle(&apos;Loading...&apos;)
    if (error || articles.length == 0) {
      if (error_code === &apos;HeadersWithoutKey&apos;) {
        setMessageTitle(&apos;No API key provided to fetch News &#x1F9E9; &apos;)
      } else if (error_code === &apos;ConcurrencyViolation&apos; &amp;&amp; articles.length == 0) {
        setMessageTitle(&apos;API Error Wait and then refresh &#x26F3;&#xFE0F;&apos;)
      } else {
        setMessageTitle(&apos;Latest updates&apos;)
      }
    } else {
      setMessageTitle(&apos;Latest updates&apos;)
    }
  }, [articles, dispatch, error_code, error])
  

   return (
   &lt;div className=&quot;flex flex-col scrollbar-hide md:scrollbar-default&quot;&gt;
       
      ..
      ..
       
       {error &amp;&amp; (
        &lt;div className=&quot;flex font-mono font-bold text-md mx-auto w-10/12 justify-center mt-36&quot;&gt; {messageTitle}&lt;/div&gt;
      )}
      &lt;Footer /&gt;
   &lt;/div&gt;
   )
}
export default Home</code></pre><p>Let&apos;s do the same for article details:</p><pre><code class="language-javascript">...
import { useAppSelector } from &apos;../../store/hooks&apos;
import { selectArticles } from &apos;../../store/slice/articleSlice&apos;
...
// import { fakeNews } from &apos;../../utils&apos; &lt;= REMOVE THIS LINE

const NewsItem = () =&gt; {
... 
  const { articles } = useAppSelector(selectArticles)
  //const articles = fakeNews &lt;= REMOVE THIS LINE

}</code></pre><p>This produce the following result:</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-28-a--19.32.50.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 3" loading="lazy" width="2000" height="1320" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-28-a--19.32.50.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-28-a--19.32.50.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-28-a--19.32.50.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-28-a--19.32.50.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>We have all the news and the detail page</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-28-a--19.35.57.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 3" loading="lazy" width="2000" height="1367" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-28-a--19.35.57.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-28-a--19.35.57.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-28-a--19.35.57.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-28-a--19.35.57.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Now let&apos;s fix something we have on the main page. when you scroll, you will see a scrollbar that appear at the right of the screen. let&apos;s fix this behavior by hiding the scrollbar with a Tailwind CSS plugin. (Yes i know, it&apos;s possible to do this with pure CSS &#x1F606; but let me show you how to do the same with a Tailwind plugin that will work for any browser). On the terminal, run the following to install the plugin:</p><pre><code>yarn add tailwind-scrollbar-hide</code></pre><p>Open tailwind.config.js at the root of the project and register the plugin</p><pre><code class="language-javascript">module.exports = {

...

plugins:[require(&apos;@tailwindcss/line-clamp&apos;), require(&apos;tailwind-scrollbar-hide&apos;)]

}</code></pre><p>Now update <code>src/pages/index.tsx</code> file with the following</p><pre><code class="language-javascript">...

const Home: NextPage = () =&gt; {

...

return (
	&lt;div className=&quot;flex flex-col scrollbar-hide&quot;&gt;
    
    ... .. // Content here
    
    &lt;/div&gt;
)
}

export default Home
</code></pre><p>And that&apos;s all. we always have everything working fine. Push everything on github and merge with the main branch. If the merge is not possible, merge locally by resolving conflicts and then retry.</p><h2 id="3-deploy-on-amazon-amplify">3. Deploy on Amazon Amplify</h2><p>On the <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-1/">first part</a> of this tutorials series, we completed how to configure and setup amazon amplify with the project. If you did&apos;nt complete this part, you will not be able to complete this part. make sure to complete the Part 1 with Amplify setup.</p><p>To deploy our app to amplify, let&apos;s add the NewsCatecherAPI key on Amplify console with the following steps</p><ol><li>Open the <a href="https://console.aws.amazon.com/amplify/" rel="noopener noreferrer">Amplify console</a>.</li><li>In the Amplify console, choose <strong>App Settings</strong>, and then choose <strong>Environment variables</strong>.</li><li>In the <strong>Environment variables</strong> section, choose <strong>Manage variables</strong>.</li><li>In the <strong>Manage variables</strong> section, under <strong>Variable</strong>, enter <code>NEXT_PUBLIC_NEWS_API_KEY</code> key. For <strong>Value</strong>, enter the value of your API Key value. By default, Amplify applies the environment variables across all branches, so you don&#x2019;t have to re-enter variables when you connect a new branch.</li></ol><p>Next, configure the project: run <code>amplify configure project</code></p><pre><code class="language-javascript">
? Which setting do you want to configure? Project information
? Enter a name for the project xxxxxxxxxxxxx // &lt;= project name on amplify console
? Choose your default editor: Visual Studio Code // or your IDE
? Choose the type of app that you&apos;re building javascript
Please tell us about your project
? What javascript framework are you using react
? Source Directory Path:  src
? Distribution Directory Path: .next // &lt;= `.next (VERY IMPORTANT)` directory 
? Build Command:  npm run-script build
? Start Command: npm run-script start
Using default provider  awscloudformation

Successfully made configuration changes to your project.</code></pre><p>NB: Note that for most of the options Amplify should be able to infer the right choice. According to <a href="https://docs.amplify.aws/guides/hosting/nextjs/q/platform/js/#getting-started">Amplify documentation</a>, some updates are required &#xA0;wether we are deploying <strong>SSG Only</strong> or <strong>SSG + SSR</strong> Next.js app. To be more precise, here are the different scenarios:</p><ul><li><strong>SSG Only</strong></li></ul><p>Inside <code>package.json</code></p><pre><code class="language-js">&quot;scripts&quot;: {
  &quot;build&quot;: &quot;next build &amp;&amp; next export&quot;,
  ...
}
</code></pre><p>Run this command.</p><pre><code class="language-sh">amplify configure project
</code></pre><p>Then, keep every thing the way it is, except this:</p><pre><code class="language-sh">Distribution Directory Path: out
</code></pre><ul><li><strong>SSG &amp; SSR</strong>:</li></ul><p>Inside <code>package.json</code></p><pre><code class="language-js">&quot;scripts&quot;: {
  &quot;build&quot;: &quot;next build&quot;,
  ...
}
</code></pre><p>Run this command.</p><pre><code class="language-sh">amplify configure project
</code></pre><p>Then, keep every thing the way it is, except this:</p><pre><code class="language-sh">Distribution Directory Path: .next</code></pre><p>In our case, we are using SSG + SSR so we are going<strong> </strong>to set the distribution directory path to &#xA0;<code>.next</code> &#xA0;- that&apos;s where <code>next export</code> spits out our static files.</p><p>Now we just have to publish with the command <code>amplify publish</code>. The amplify CLI with proceed some steps and you prompt a success message once everything done. </p><pre><code>&#x2714; Deployment complete!
https://dev.xxxxxx_app_id.amplifyapp.com</code></pre><p><strong>NB</strong>: Another way is to trigger the deploy manually on the Amplify console when everything is pushed on the <strong>configured branch (</strong>In our case, we configured the main branch on the <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-1/">part 1</a> <strong>)</strong> of your github repository.</p><p>We have completed the third Part of this tutorial series &#x1F44F;</p><p>Thanks for reading. </p><p><em>Follow me on socials media to stay up to date with latest posts.</em></p>]]></content:encoded></item><item><title><![CDATA[Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 2]]></title><description><![CDATA[<p>Hey guys &#x1F91A;, Glad to have you here.</p><p>In the previous post, we covered the <a href="https://blog.foujeupavel.com/create-and-deploy-a-fullstack-web-application-on-aws/">Part 1</a> of this series about building a complete Fullstack app called <a href="https://dribbble.com/shots/14700802-News-Section/attachments/6399696?mode=media">FoxNews</a> &#xA0;using Amazon Web Services. The Part 1 was focused on setting our tools and host the basic setup of our Next.</p>]]></description><link>https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-2/</link><guid isPermaLink="false">629d1ca4a72f86064975c643</guid><category><![CDATA[Front-end]]></category><category><![CDATA[Typescript]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Graphql]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Sat, 18 Jun 2022 22:46:11 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1638913970895-d3df59be1466?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wxfDF8YWxsfDF8fHx8fHwyfHwxNjU1NTAzMDY2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1638913970895-d3df59be1466?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wxfDF8YWxsfDF8fHx8fHwyfHwxNjU1NTAzMDY2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 2"><p>Hey guys &#x1F91A;, Glad to have you here.</p><p>In the previous post, we covered the <a href="https://blog.foujeupavel.com/create-and-deploy-a-fullstack-web-application-on-aws/">Part 1</a> of this series about building a complete Fullstack app called <a href="https://dribbble.com/shots/14700802-News-Section/attachments/6399696?mode=media">FoxNews</a> &#xA0;using Amazon Web Services. The Part 1 was focused on setting our tools and host the basic setup of our Next.js app on Amazon Amplify using Amplify Framework. In this part, we will be building the main logic of our app using Next.js and Tailwind CSS. The main purpose of this part will be : </p><ul><li>Being able to Fetch and display news from external API. We will use <a href="https://newscatcherapi.com/">https://newscatcherapi.com/</a> to fetch randoms news.</li><li>Display some specifics details about a news</li></ul><p>Some usefull tools we will be using in this part</p><ul><li>Next.js &#xA0;</li><li>Tailwind CSS to style everything</li><li>NewscatcherAPI to fetch Random news (For now, we will add some logic to create and store news on our own remote server later &#x1F609;) </li></ul><h2 id="1-put-everything-in-place">1. Put everything in place</h2><p>As we are using existing project, we just need to clone the project at the stage we left in our <a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-1/">previous post</a> where we hosted the basic version of our Next.js App. To do so, run this command inside the terminal</p><pre><code class="language-javascript">git clone -b feat/part1-setup-with-amplify --single-branch https://github.com/Doha26/Foxnews-nextjs-aws.git</code></pre><p>With this, you fetch all the branches in the repository, checkout to the one you specified, and the specific branch becomes the configured local branch for <code>git push</code> and <code>git pull</code> The <code>--single-branch</code> flag (introduced in Git version 1.7.10) allows you to only fetch files from the specified branch without fetching other branches</p><p>Go tot the root of the project with <code>cd foxnews-nextjs-aws and yarn</code> &#xA0;</p><p>Create a new branch to track all the changes we will make during this Part 2. To do so, run this inside the terminal. </p><ul><li><code>git checkout -b feat/part2-ui-layer</code> to create a new branch called <code>feat/part2-ui-layer</code></li><li><code>git push --set-upstream origin feat/part2-ui-layer</code> push everything on github to start</li><li>run yarn dev to serve the default setup at <code>localhost:3000</code> that looks like this </li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-18-a--11.57.27.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 2" loading="lazy" width="1233" height="776" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-18-a--11.57.27.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-18-a--11.57.27.png 1000w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-18-a--11.57.27.png 1233w" sizes="(min-width: 720px) 720px"></figure><p>Well &#x1F44F;, if you have this, it&apos;s means we are ready for the next step. Let&apos;s go &#x1F3C4;&#x1F3FC;&#x200D;&#x2642;&#xFE0F;</p><h2 id="2-setup-tailwind-css">2. Setup Tailwind CSS </h2><p>In this section, we are going to build the UI Layer of this second part with next.Js and Tailwind CSS. First of all we are going to create our folder structure and replace the default one provided by Next.js &#xA0;with the following steps:</p><ul><li>move <code>/pages</code> folder inside <code>src</code> folder</li><li>create a new folder named <code>/ui</code> inside <code>src</code> and inside the <code>ui</code> folder, create another folder named <code>component</code> that will host our components</li></ul><p>With this setup, we are good to go. Let&apos;s setup tailwind CSS to work with our Next.js setup. According to Tailwind documentation <a href="https://tailwindcss.com/docs/guides/nextjs">here</a>, it&apos;s very simple to do.</p><ul><li><strong>Install Tailwind CSS</strong>: &#xA0;To install Tailwind and its peer dependencies, Run this on your favorite terminal <code>yarn add -D tailwindcss postcss autoprefixer</code> and then run the init command to generate both <code>tailwind.config.js</code> and <code>postcss.config.js</code> with &#xA0;<code>npx tailwindcss init -p</code> </li><li><strong>Configure your template paths</strong>: Add the paths to all of your template files in your <code>tailwind.config.js</code> file. </li></ul><pre><code class="language-javascript">module.exports = {
  content: [  // ADD this content block
    &quot;./src/pages/**/*.{js,ts,jsx,tsx}&quot;,  
    &quot;./src/ui/components/**/*.{js,ts,jsx,tsx}&quot;, 
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}</code></pre><ul><li><strong>Add the Tailwind directives to the global CSS fil</strong>e : add the following inside &#xA0;<code>./styles/globals.css</code> file</li></ul><pre><code class="language-css">@tailwind base;
@tailwind components;
@tailwind utilities;</code></pre><p>That&apos;s all. Tailwind is now set with our Next.js setup. Let&apos;s add custom font and styles that we will use in our app. For the font, we will use <a href="https://fonts.google.com/specimen/Inter">Inter</a>, a free google font. Next, create a file name <code>_document.tsx</code> inside <code>/src/pages</code> folder and add the following:</p><pre><code class="language-javascript">import Document, { Html, Head, Main, NextScript } from &apos;next/document&apos;
import { DocumentContext } from &apos;next/document&apos;
class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const initialProps = await Document.getInitialProps(ctx)
    return initialProps
  }

  render() {
    return (
      &lt;Html&gt;
        &lt;Head&gt;
          &lt;link href=&quot;https://fonts.googleapis.com/css2?family=Inter&amp;display=swap&quot; rel=&quot;stylesheet&quot; /&gt; 
        &lt;/Head&gt;
        &lt;body&gt;
          &lt;Main /&gt;
          &lt;NextScript /&gt;
        &lt;/body&gt;
      &lt;/Html&gt;
    )
  }
}

export default MyDocument
</code></pre><p>This will override the default <code>Document</code> object and update the <code>html</code> of our page to import the font we need. Now Open <code>tailwind.config.js</code> file and add the following: </p><pre><code class="language-javascript">/** @type {import(&apos;tailwindcss&apos;).Config} */
const defaultTheme = require(&apos;tailwindcss/defaultTheme&apos;)
module.exports = {
  ..
  theme: { 
    extend: {
      fontFamily: { // Add this to register the theme with tailwind
        sans: [&apos;Inter&apos;, ...defaultTheme.fontFamily.sans]
      }
    },
    colors: { // Add some colors with variant
      description: &apos;#54595F&apos;,
      white: &apos;#fff&apos;,
      black: &apos;#000&apos;,
      grey: {
        100: &apos;#cfd8dc&apos;
      },
      blue: {
        400: &apos;#5c6bc0&apos;
      },
      red: {
        400: &apos;#ef5350&apos;,
        500: &apos;#f44336&apos;
      }
    }
  },
  plugins: []
}
</code></pre><p>That&apos;s all, we are our Font and Tailwind set with the project</p><h2 id="3-create-components">3. Create components </h2><p>Inside <code>src/ui/components</code> create the following functional components.</p><ul><li><strong>Footer.tsx</strong></li></ul><pre><code class="language-javascript">import Image from &apos;next/image&apos;
import Link from &apos;next/link&apos;

const Footer = () =&gt; (
  &lt;nav className=&quot;mb-0 fixed w-full z-10 bottom-0 bg-black &quot;&gt;
    &lt;div className=&quot;flex flex-row items-center justify-between h-10 sm:w-9/12 sm:mx-auto mx-4 &quot;&gt;
      &lt;Image src=&quot;/logo-img.svg&quot; alt=&quot;Logo&quot; width=&quot;25&quot; height=&quot;25&quot; /&gt;
      &lt;Link href=&quot;&quot;&gt;
        &lt;h1 className=&quot;font-mono font-bold text-sm hidden md:block text-white cursor-pointer&quot;&gt;&#xA9; Your Name :)&lt;/h1&gt;
      &lt;/Link&gt;
    &lt;/div&gt;
  &lt;/nav&gt;
)

export default Footer
</code></pre><ul><li><strong>Header.tsx</strong></li></ul><pre><code class="language-javascript">import Head from &apos;next/head&apos;

const Header = () =&gt; (
  &lt;Head&gt;
    &lt;title&gt;FoxNews&lt;/title&gt;
    &lt;meta name=&quot;description&quot; content=&quot;Generated by create next app&quot; /&gt;
    &lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;
  &lt;/Head&gt;
)

export default Header
</code></pre><ul><li><strong>Nav.tsx</strong></li></ul><pre><code class="language-javascript">import Image from &apos;next/image&apos;
import Link from &apos;next/link&apos;

const Nav = () =&gt; (
  &lt;nav className=&quot;mt-0 fixed w-full z-10 top-0 bg-black &quot;&gt;
    &lt;div className=&quot;flex flex-row items-center justify-between h-16 sm:w-9/12 sm:mx-auto mx-4 &quot;&gt;
      &lt;div className=&quot;flex flex-row&quot;&gt;
        &lt;Image src=&quot;/logo-img.svg&quot; alt=&quot;Logo&quot; width=&quot;36&quot; height=&quot;36&quot; /&gt;
        &lt;Link href=&quot;/&quot;&gt;
          &lt;a className=&quot;font-mono font-bold text-3xl p-2 hidden sm:block cursor-pointer text-white&quot;&gt;FoxNews&lt;/a&gt;
        &lt;/Link&gt;
      &lt;/div&gt;
      &lt;h1 className=&quot;font-mono font-bold text-sm hidden md:block text-white&quot;&gt;
        Call Us (348)981.872 / hello@foxnews.com
      &lt;/h1&gt;
      &lt;h1 className=&quot;font-mono font-bold text-sm hover:underline hover:cursor-pointer hover:font-bold text-white&quot;&gt;
        Login
      &lt;/h1&gt;
    &lt;/div&gt;
  &lt;/nav&gt;
)

export default Nav
</code></pre><h2 id="4-the-main-page">4. The main page </h2><p>Inside <code>/src/pages/index.tsx</code> add the following code to import our created components</p><pre><code class="language-javascript">import Header from &apos;../ui/components/Header&apos;
import type { NextPage } from &apos;next&apos;
import Nav from &apos;../ui/components/Nav&apos;
import Footer from &apos;../ui/components/Footer&apos;

const Home: NextPage = () =&gt; {
  return (
    &lt;div className=&quot;flex flex-col&quot;&gt;
      &lt;Header /&gt;
      &lt;Nav /&gt;
      &lt;Footer /&gt;
    &lt;/div&gt;
  )
}
export default Home</code></pre><p>And we have the following result that display only the Navbar and the footer</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-18-a--14.07.09.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 2" loading="lazy" width="2000" height="1119" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-18-a--14.07.09.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-18-a--14.07.09.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-18-a--14.07.09.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-18-a--14.07.09.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Now, let&apos;s create &#xA0;<code>NewsItem</code> component. For this we need to first create associated type. &#xA0;Create a file name <code>utils.ts</code> inside <code>src</code> folder. &#xA0;According to NewsCatcherAPI, a <strong>News</strong> type will have the following attribute. </p><pre><code class="language-javascript">export type NewsAPIObject = {
  media: string
  title: string
  summary?: string
  authorImg?: string
  author?: string
  authorRole?: string
  hasImage?: boolean
  largeImage?: boolean
  itemIndex?: number
  link?: string
  published_date?: string
  topic?: string
  country?: string
  language?: string
  _id?: string
  _score?: number
  twitter_account?: string
  clean_url?: string
  expand?: boolean
}

export type NewsAPIResponse = {
  status?: string
  total_pages?: number
  page_size?: number
  articles?: NewsAPIObject[]
}
</code></pre><p>Now create <code>NewsItem.tsx</code> inside <code>src/ui/components</code>. Before adding html inside our component, there is 2 things to note. </p><ul><li>A NewsItem will have a &#xA0;description that will not be full displayed. We will display 150 characters and add &#xA0;a <code>Read more &gt;&gt;</code> &#xA0;button that will hide or display the full description. to do so, we will use create a function called <code>toggleReadMore()</code> to change the state according to if the button <code>Read More &gt;&gt;</code> is clicked or not and use a plugin that provides utilities for visually truncating text after a fixed number of lines. </li><li>A NewsItem also have a <code>published_date</code> and we will format it using <a href="https://momentjs.com/">moment.js</a></li></ul><p>let&apos;s install required lib. Run the following on your terminal: <code>yarn add moment @tailwindcss/line-clamp</code>. </p><p>As <code>@tailwindcss/line-clamp</code> is a Tailwind CSS plugin, we need to register it inside <code>tailwind.config.js</code> </p><pre><code>/** @type {import(&apos;tailwindcss&apos;).Config} */
module.exports = {
  theme: {
    ..
  },
  plugins: [require(&apos;@tailwindcss/line-clamp&apos;))] // &lt;= TAILWIND PLUGINS
}
</code></pre><p>The <code>NewsItem.tsx</code> file will have the following </p><pre><code class="language-javascript">import { useState } from &apos;react&apos;
import moment from &apos;moment&apos;
import { NewsAPIObject } from &apos;../../utils&apos;

const News = ({ media, title, summary, author, country, published_date, expand }: NewsAPIObject) =&gt; {
  const [isReadMore, setIsReadMore] = useState(false)
  const toggleReadMore = () =&gt; {
    setIsReadMore(!isReadMore)
  }

  return (
    &lt;div
      className={`flex flex-col ${
        !expand ? &apos;cursor-pointer transition hover:scale-105 duration-300 ease-in-out delay-50 mt-4&apos; : &apos;mt-8&apos;
      }`}
    &gt;
      &lt;img className={`object-cover rounded w-full ${expand ? &apos;h-80&apos; : &apos;h-48&apos;}`} src={media} alt=&quot;Img&quot; /&gt;
      &lt;h1
        className={`line-clamp-3 py-3 font-Inter font-semibold ${
          expand ? &apos;text-3xl&apos; : &apos;text-2xl&apos;
        } text-black hover:text-primary text-left text-ellipsis overflow-clip `}
      &gt;
        {title}
      &lt;/h1&gt;
      &lt;p className=&quot;line-clamp-4 font-medium text-base text-description  mb-5 text-left&quot;&gt;
		 {isReadMore ? summary?.slice(0, 150) : summary}
        &lt;span onClick={() =&gt; toggleReadMore()} className=&quot;font-semibold text-sm cursor-pointer text-blue-400&quot;&gt;
          {isReadMore ? &apos;...read more&apos; : &apos; show less&apos;}
        &lt;/span&gt;
      &lt;/p&gt;
      &lt;div className=&quot;flex items-center&quot;&gt;
        &lt;img className=&quot;rounded-lg w-10 h-10 object-cover&quot; src={media} alt=&quot;Img&quot; /&gt;
        &lt;div className=&quot;flex flex-col ml-2&quot;&gt;
          &lt;strong&gt;
            {author} &lt;span className=&quot;text-xs text-description&quot;&gt;| {country}&lt;/span&gt;
          &lt;/strong&gt;
          &lt;span className=&quot;text-sm text-description&quot;&gt;{moment(published_date).format(&apos;MMMM Do YYYY, h:mm:ss a&apos;)}&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  )
}

export default News
</code></pre><p>Create some data inside <code>src/utils.ts</code> with the following:</p><pre><code class="language-javascript">..

export const fakeNews = [
  {
    title: &quot;Chelsea Marcantel&apos;s The Upstairs Department Finishes World Premiere Run June 12&quot;,
    author: &apos;Leah Putnam&apos;,
    published_date: &apos;2022-06-12 04:30:00&apos;,
    link: &apos;https://playbill.com/article/chelsea-marcantels-the-upstairs-department-finishes-world-premiere-run-june-12&apos;,
    clean_url: &apos;playbill.com&apos;,
    summary:
      &apos;Regional News Regional News Regional News Regional News Regional News Video Los Angeles News Regional News Regional News Regional News Awards Video Regional News Regional News Video Video Regional News Production Photos Production Photos Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Los Angeles News Regional News Regional News Production Photos Cast Recordings &amp;&apos;,
    topic: &apos;news&apos;,
    country: &apos;US&apos;,
    language: null,
    media: &apos;https://assets.playbill.com/editorial/_1200x630_crop_center-center_82_none/1cVRwy-4.jpeg?mtime=1648834896&apos;,
    authorImg:
      &apos;https://assets.playbill.com/editorial/_1200x630_crop_center-center_82_none/1cVRwy-4.jpeg?mtime=1648834896&apos;,
    twitter_account: &apos;@playbill&apos;,
    _score: 5.424161,
    _id: &apos;c17698032111425760dac1cf58bd8778&apos;
  },
  {
    title: &quot;Britta Johnson&apos;s Life After Begins Previews at Chicago&apos;s Goodman Theatre June 11&quot;,
    author: &apos;Leah Putnam&apos;,
    published_date: &apos;2022-06-11 04:01:28&apos;,
    link: &apos;https://playbill.com/article/britta-johnsons-life-after-begins-previews-at-chicagos-goodman-theatre-june-11&apos;,
    clean_url: &apos;playbill.com&apos;,
    summary:
      &apos;Regional News Regional News Regional News Regional News Video Los Angeles News Regional News Regional News Regional News Awards Video Regional News Regional News Video Video Regional News Production Photos Production Photos Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Regional News Los Angeles News Regional News Regional News Production Photos Cast Recordings &amp; Albums Region&apos;,
    topic: &apos;science&apos;,
    country: &apos;US&apos;,
    language: &apos;en&apos;,
    authorImg:
      &apos;https://assets.playbill.com/editorial/_1200x630_crop_center-center_82_none/1cVRwy-4.jpeg?mtime=1648834896&apos;,
    media:
      &apos;https://assets.playbill.com/editorial/_1200x630_crop_center-center_82_none/Samantha-Williams-Paul-Alexander-Nolan-and-Bryonha-Marie-Parham_HR.jpg?mtime=1654788330&apos;,
    twitter_account: &apos;@playbill&apos;,
    _score: 5.418454,
    _id: &apos;f54468ea81e6026241e6af6b2bb07848&apos;
  }
]
</code></pre><p>Update <code>src/index.tsx</code> to use the new component. </p><pre><code class="language-javascript">
..
import { NewsAPIObject } from &apos;../utils&apos;
import Link from &apos;next/link&apos;
import News from &apos;../ui/components/NewsItem&apos;
import { fakeNews, NewsAPIObject } from &apos;../utils&apos;

const articles: NewsAPIObject[] = fakeNews

const Home: NextPage = () =&gt; {
return (
    &lt;div className=&quot;flex flex-col&quot;&gt;
      &lt;Header /&gt;
      &lt;Nav /&gt;
      {articles?.length &amp;&amp; (
        &lt;div className=&quot; flex justify-center font-bold mt-24 text-2xl underline text-black items-center&quot;&gt;
          {messageTitle}
          &lt;span className=&quot;flex h-3 w-3 ml-2&quot;&gt;
            &lt;div className=&quot;animate-ping inline-flex h-3 w-3 rounded-full bg-red-400 opacity-75&quot;&gt;&lt;/div&gt;
            &lt;span className=&quot;absolute rounded-full h-3 w-3 bg-red-500&quot;&gt;&lt;/span&gt;
          &lt;/span&gt;
        &lt;/div&gt;
      )}

      {articles?.length &amp;&amp; (
        &lt;div className=&quot;sm:px-16 py-8 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3  gap-6 mx-auto w-10/12 mb-32&quot;&gt;
          {articles?.map((item: NewsAPIObject) =&gt; {
            return (
              &lt;Link href={`/news/${item._id}`} key={item._id}&gt;
                &lt;a&gt;
                  {/* To avoid forwardRef issue with Next/link*/}{&apos; &apos;}
                  &lt;News
                    media={item.media}
                    title={item.title}
                    summary={item.summary}
                    author={item.author}
                    authorImg={item.authorImg}
                    country={item.country}
                    _id={item._id}
                    published_date={item.published_date}
                    expand={false}
                  /&gt;
                &lt;/a&gt;
              &lt;/Link&gt;
            )
          })}
        &lt;/div&gt;
      )}
      {error &amp;&amp; (
        &lt;div className=&quot;flex font-mono font-bold text-md mx-auto w-10/12 justify-center mt-36&quot;&gt; {messageTitle}&lt;/div&gt;
      )}
      &lt;Footer /&gt;
    &lt;/div&gt;
  )
}
export default Home</code></pre><p>At his step, let&apos;s add a title for this page. Inside the <code>index.tsx</code> </p><pre><code class="language-javascript">import { useState } from &apos;react&apos;

const Home: NextPage = () =&gt; {
const [messageTitle, setMessageTitle] = useState(&apos;Latest updates&apos;)
 
 return (
     ..
   &lt;Nav/&gt;
     {articles?.length &amp;&amp; (
        &lt;div className=&quot; flex justify-center font-bold mt-24 text-2xl underline text-black items-center&quot;&gt;
          {messageTitle}
          &lt;span className=&quot;flex h-3 w-3 ml-2&quot;&gt;
            &lt;div className=&quot;animate-ping inline-flex h-3 w-3 rounded-full bg-red-400 opacity-75&quot;&gt;&lt;/div&gt;
            &lt;span className=&quot;absolute rounded-full h-3 w-3 bg-red-500&quot;&gt;&lt;/span&gt;
          &lt;/span&gt;
        &lt;/div&gt;
      )}
 )
}</code></pre><p>The result look like this</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-18-a--17.25.13.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 2" loading="lazy" width="2000" height="1123" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-18-a--17.25.13.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-18-a--17.25.13.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-18-a--17.25.13.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-18-a--17.25.13.png 2400w" sizes="(min-width: 720px) 720px"></figure><p></p><h2 id="5-the-detail-page">5. The detail page</h2><p>Next.js has a file-system based router built on the <a href="https://nextjs.org/docs/basic-features/pages">concept of pages</a> with two essentials things to know about Routing.</p><ul><li>Each file inside pages directory is associated with a route based on its file name. This means, if you create <code>pages/about.tsx</code> that exports a React component like below, and it will be accessible at <code>/about</code> </li><li>Dynamic routes : if you create a file called <code>pages/posts/[id].js</code>, then it will be accessible at <code>posts/1</code>, <code>posts/2</code>, etc.</li><li>To learn more about dynamic routing, check the <a href="https://nextjs.org/docs/routing/dynamic-routes">Dynamic Routing documentation</a></li></ul><p>So, When a file is added to the <code>pages</code> directory, it&apos;s automatically available as a route.</p><p>The files inside the <code>pages</code> directory can be used to define most common patterns.</p><h4 id="index-routes"><a href="https://nextjs.org/docs/routing/introduction#index-routes">Index routes</a></h4><p>The router will automatically route files named <code>index</code> to the root of the directory.</p><ul><li><code>pages/index.js</code> &#x2192; <code>/</code></li><li><code>pages/blog/index.js</code> &#x2192; <code>/blog</code></li></ul><h4 id="nested-routes"><a href="https://nextjs.org/docs/routing/introduction#nested-routes">Nested routes</a></h4><p>The router supports nested files. If you create a nested folder structure, files will automatically be routed in the same way still.</p><ul><li><code>pages/blog/first-post.js</code> &#x2192; <code>/blog/first-post</code></li><li><code>pages/dashboard/settings/username.js</code> &#x2192; <code>/dashboard/settings/username</code></li></ul><h4 id="dynamic-route-segments"><a href="https://nextjs.org/docs/routing/introduction#dynamic-route-segments">Dynamic route segments</a></h4><p>To match a dynamic segment, you can use the bracket syntax. This allows you to match named parameters.</p><ul><li><code>pages/blog/[slug].js</code> &#x2192; <code>/blog/:slug</code> (<code>/blog/hello-world</code>)</li><li><code>pages/[username]/settings.js</code> &#x2192; <code>/:username/settings</code> (<code>/foo/settings</code>)</li><li><code>pages/post/[...all].js</code> &#x2192; <code>/post/*</code> (<code>/post/2020/id/title</code>)</li></ul><p>Check out the <a href="https://nextjs.org/docs/routing/dynamic-routes">Dynamic Routes documentation</a> to learn more about how they work.</p><p>So Let&apos;s create the detail page. In <code>src/pages</code> create a new folder named <code>news</code> and add a file named <code>[id].tsx</code> and add the following.</p><pre><code class="language-javascript">import Image from &apos;next/image&apos;
import { useRouter } from &apos;next/router&apos;
import { fakeNews } from &apos;../utils&apos;
import Header from &apos;../../ui/components/Header&apos;
import Nav from &apos;../../ui/components/Nav&apos;
import News from &apos;../../ui/components/NewsItem&apos;
import Link from &apos;next/link&apos;

const articles: NewsAPIObject[] = fakeNews // we will update later

interface IQueryParam {
  id: string
}
const NewsItem = () =&gt; {
  const router = useRouter()
  const { id } = router.query as unknown as IQueryParam
  
  const item = articles?.find(article =&gt; article._id === id)
  
  return (
    &lt;div className=&quot;flex flex-col&quot;&gt;
      &lt;Header /&gt;
      &lt;Nav /&gt;
      &lt;div className=&quot;sm:mx-auto md:w-11/12 sm:w-11/12 mt-32 lg:w-6/12&quot;&gt;
        &lt;Link href=&quot;/&quot; title=&quot;Back to Home&quot;&gt;
          &lt;Image src=&quot;/back.svg&quot; alt=&quot;Logo&quot; width=&quot;39&quot; height=&quot;32&quot; className=&quot;cursor-pointer&quot; /&gt;
        &lt;/Link&gt;
        {item &amp;&amp; (
          &lt;News
            media={item.media}
            title={item.title}
            summary={item.summary}
            author={item.author}
            authorImg={item.authorImg}
            country={item.country}
            _id={item._id}
            published_date={item.published_date}
            expand={true}
          /&gt;
        )}
      &lt;/div&gt;
    &lt;/div&gt;
  )
}

export default NewsItem
</code></pre><p>That&apos;s all. if we click on one item on the main page, it will display the follow result</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-18-a--22.34.50.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 2" loading="lazy" width="2000" height="1278" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-18-a--22.34.50.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-18-a--22.34.50.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-18-a--22.34.50.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-18-a--22.34.50.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Now lets add some pagination Item at the bottom to navigate over Previous and Next News </p><p>Inside <code>src/ui/components</code> create two components</p><ul><li><strong>PaginationItem.tsx</strong></li></ul><pre><code class="language-javascript">import moment from &apos;moment&apos;
import Link from &apos;next/link&apos;

interface PaginationItemProps {
  position: &apos;right&apos; | &apos;left&apos;
  title?: string
  mediaSource?: string
  date?: string
  id?: string
}
const PaginationItem = ({ position, title, mediaSource, date, id }: PaginationItemProps) =&gt; {
  const image = mediaSource as string
  return position == &apos;left&apos; ? (
    &lt;Link href={`/news/${id}`}&gt;
      &lt;div className=&quot;flex max-w-sm cursor-pointer transition hover:scale-105 duration-300 ease-in-out delay-50 w-full&quot;&gt;
        {image &amp;&amp; &lt;img className=&quot;rounded w-16 h-16&quot; src={image} alt=&quot;Img&quot; /&gt;}
        &lt;div className=&quot;flex flex-col ml-2 max-w-xs hover:underline justify-items-start &quot;&gt;
          &lt;strong className=&quot;line-clamp-3 text-ellipsis overflow-hidden&quot;&gt;{title}&lt;/strong&gt;
          &lt;span className=&quot;text-sm text-description&quot;&gt;{moment(date).format(&apos;MMMM Do YYYY, h:mm:ss a&apos;)}&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/Link&gt;
  ) : (
    &lt;Link href={`/news/${id}`}&gt;
      &lt;div className=&quot;flex mt-10 md:mt-0 cursor-pointer w-full transition hover:scale-105 duration-300 ease-in-out delay-50 &quot;&gt;
        &lt;div className=&quot;flex flex-col  mr-2 max-w-xs hover:underline text-right &quot;&gt;
          &lt;strong className=&quot;line-clamp-3&quot;&gt;{title}&lt;/strong&gt;
          &lt;span className=&quot;text-sm &quot;&gt;{moment(date).format(&apos;MMMM Do YYYY, h:mm:ss a&apos;)}&lt;/span&gt;
        &lt;/div&gt;
        {image &amp;&amp; &lt;img className=&quot;rounded pl-22 w-16 h-16 &quot; src={image} alt=&quot;Img&quot; /&gt;}
      &lt;/div&gt;
    &lt;/Link&gt;
  )
}

export default PaginationItem
</code></pre><ul><li><strong>PaginationBottom.tsx</strong></li></ul><pre><code class="language-javascript">import { NewsAPIObject } from &apos;../../utils&apos;
import PaginationItem from &apos;./PaginationItem&apos;

interface IPaginationBottomProps {
  leftItem?: NewsAPIObject
  rightItem?: NewsAPIObject
}
const PaginationBottom = ({ leftItem, rightItem }: IPaginationBottomProps) =&gt; {
  const {
    title: leftItemTitle,
    media: leftItemMediaSource,
    published_date: leftItemDate,
    _id: leftItemId
  } = leftItem as NewsAPIObject
  const {
    title: rightItemTitle,
    media: rightItemMediaSource,
    published_date: rightItemDate,
    _id: rightItemId
  } = rightItem as NewsAPIObject

  return (
    &lt;div className=&quot;mt-10 mb-20 md:flex flex-row justify-between w-full&quot;&gt;
      &lt;PaginationItem
        position=&quot;left&quot;
        title={leftItemTitle}
        mediaSource={leftItemMediaSource}
        date={leftItemDate}
        id={leftItemId}
      /&gt;
      &lt;PaginationItem
        position=&quot;right&quot;
        title={rightItemTitle}
        mediaSource={rightItemMediaSource}
        date={rightItemDate}
        id={rightItemId}
      /&gt;
    &lt;/div&gt;
  )
}

export default PaginationBottom
</code></pre><p>Update the detail page <code>[id].tsx</code> file inside <code>src/news</code> folder</p><pre><code class="language-javascript">import PaginationBottom from &apos;../../ui/components/PaginationBottom&apos;

const NewsItem = () =&gt; {
.. 
const item = articles?.find(article =&gt; article._id === id)
  const itemIndex = articles?.findIndex(article =&gt; article._id === id)
  const leftItem = itemIndex === 0 ? item : articles[itemIndex - 1]
  const rightItem = itemIndex == articles.length - 1 ? item : articles[itemIndex + 1]
  
   return (
   &lt;div className=&quot;sm:mx-auto md:w-11/12 sm:w-11/12 mt-32 lg:w-6/12&quot;&gt;
   ..
   &lt;PaginationBottom leftItem={leftItem} rightItem={rightItem} /&gt; // &lt;= THIS LINE
   &lt;/div&gt;
   )

}
export default NewsItem</code></pre><p>And this produce the following result</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-18-a--22.57.31.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 2" loading="lazy" width="2000" height="1358" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-18-a--22.57.31.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-18-a--22.57.31.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-18-a--22.57.31.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-18-a--22.57.31.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>We have completed this Second Part of the tutorial. In the next tutorial, we will see how to setup Redux with Redux toolkit.</p><p>Thanks for reading. </p><p><em>Follow me on socials media to stay up to date with latest posts.</em></p>]]></content:encoded></item><item><title><![CDATA[Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1]]></title><description><![CDATA[<p>Hey guys &#x1F91A;, Glad to have you here. In this post, we are going to cover how to build and deploy a Fullstack web application using Amazon Web Services. &#xA0;</p><p>We are going to build an app called FoxNews, step by step. The final result will look like this:</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png" class="kg-image" alt loading="lazy" width="2000" height="925" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png 2400w" sizes="(min-width: 720px) 720px"></figure><p> This</p>]]></description><link>https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-1/</link><guid isPermaLink="false">62976ccfa72f86064975c133</guid><category><![CDATA[Front-end]]></category><category><![CDATA[Typescript]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Graphql]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Sat, 04 Jun 2022 00:46:02 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1638913970895-d3df59be1466?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wxfDF8YWxsfDF8fHx8fHwyfHwxNjU1NTAzMDY2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1638913970895-d3df59be1466?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wxfDF8YWxsfDF8fHx8fHwyfHwxNjU1NTAzMDY2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1"><p>Hey guys &#x1F91A;, Glad to have you here. In this post, we are going to cover how to build and deploy a Fullstack web application using Amazon Web Services. &#xA0;</p><p>We are going to build an app called FoxNews, step by step. The final result will look like this:</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="925" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-11-a--23.09.37.png 2400w" sizes="(min-width: 720px) 720px"></figure><p> This post will be the first in a long series (4 Parts) covering the use of Amazon Web Services</p><ul><li><a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-1/">Part </a>1 : We will setup our Dev environment and deploy the v1 of our app on AWS Amplify using Amplify Framework.</li><li><a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-2/">Part2</a>: Will be focused on building the main logic of our app using Next.js and Tailwind CSS.</li><li><a href="https://blog.foujeupavel.com/build-and-deploy-a-fullstack-app-with-typescript-next-js-graphql-tailwind-css-and-aws-part-3/">Part3</a> : We will be able to fetch and displays news from remote API and setup Redux with Redux Toolkit to persist news.</li><li>Part 4 (Upcoming): Will be focused on creating a complete authentication system &#xA0;with login, signing, password recovery using Amazon Cognito.</li></ul><p> Here bellow is the list of services we will use: </p><ul><li><a href="https://aws.amazon.com/fr/amplify/">Amazon Amplify Framework</a> A &#xA0;comprehensive library for building sophisticated cloud-powered apps on a flexible, scalable, and reliable serverless backend on AWS.</li><li><a href="https://aws.amazon.com/cloudfront/" rel="noopener ugc nofollow">AWS CloudFront</a><strong><strong>&#x2019;</strong></strong>s CDN to distribute around the world</li><li><a href="https://aws.amazon.com/s3/" rel="noopener ugc nofollow">AWS S3</a> to upload images</li><li><a href="https://aws.amazon.com/iam/?nc1=h_ls">AWS IAM(Identity and Access Management</a> which provides fine-grained access control across all of AWS</li><li><a href="https://aws.amazon.com/lambda/?nc2=type_a">AWS Lambda</a> to create a serverless function that will resize images befre storing them on S3</li><li><a href="https://aws.amazon.com/route53/">AWS Route 53</a><strong> &#xA0;</strong>for domain name</li><li><a href="https://aws.amazon.com/certificate-manager/">AWS Certificate Manager</a><strong> &#xA0;</strong>that will provide us an SSL certificate for the domain name</li><li><a href="https://aws.amazon.com/fr/cognito/?nc2=type_a">Amazon Cognito</a> for registration, authentication, and account recovery</li><li><a href="https://aws.amazon.com/dynamodb/?nc2=type_a">Amazon DynamoDB</a> which is is a fully managed, serverless, key-value NoSQL database designed to run high-performance applications at any scale</li><li><a href="https://aws.amazon.com/fr/appsync/">AWS AppSync</a> that will help us create and manage a GraphQL backend. In our case, we will should be able to read, create, update, delete news. </li><li><a href="https://aws.amazon.com/s3/pricing/">Amazon S3 bucket</a> to store your app&apos;s static assets</li><li><a href="https://aws.amazon.com/lambda/pricing/">Lambda@Edge function</a> to SSR pages</li><li><a href="https://nextjs.org/">Next.js</a> with Typescript as the frontend framework</li><li><a href="https://tailwindcss.com/">Tailwind CSS</a> as the css framework</li></ul><p>For this First Part, we will setup our Dev environment and deploy the v1 of our app on AWS. So let&apos;s start by setting everything we will need up !</p><h2 id="1-setup-amplify-cli">1. Setup Amplify CLI </h2><p>The Amplify Command Line Interface (CLI) is a unified toolchain to create AWS cloud services for your app. Let&apos;s go ahead and install the Amplify CLI.</p><p>Before we start coding our application, we need to setup amazon amplify on our local machine. Let&apos;s go step by step.</p><p>First go <a href="https://portal.aws.amazon.com/billing/signup?redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start">here</a> and create an AWS account if you don&apos;t have one. you will need it to create and manage apps and services on AWS</p><p>Follow thees instructions to set it up on your local machine</p><pre><code># NPM
$ npm install -g @aws-amplify/cli

# cURL (Mac &amp; Linux)
curl -sL https://aws-amplify.github.io/amplify-cli/install | bash &amp;&amp; $SHELL

# cURL (Windows)
curl -sL https://aws-amplify.github.io/amplify-cli/install-win -o install.cmd &amp;&amp; install.cmd</code></pre><p>Once completed, run <code>amplify configure</code></p><p>This will open amplify console and try to connect to <a href="https://console.aws.amazon.com/">https://console.aws.amazon.com/</a> . Once connected, it will ask you to hit Enter &#xA0; &#xA0; &#xA0; &#xA0;to continue to the second step. </p><ul><li>Specify the AWS Region. (Use arrow keys to select a region)</li><li>Specify the username for the new IAM. We will cover IAM in another blog post. But basically, IAM stand for Identity Access Management and according to AWS doc, its a service that secure access to your ressources. Follow the steps as illustrated in the following gif from AWS doc.</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/user-creation.gif" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="640" height="320"></figure><p>As recommended in the <a href="https://docs.amplify.aws/start/getting-started/installation/q/integration/next/">Amplify doc</a>, Create a user with <code>AdministratorAccess-Amplify</code> to your account to provision AWS resources for you like AppSync, Cognito etc</p><p>Once the user is created, Amplify CLI will ask you to provide the <code>accessKeyId</code> and the <code>secretAccessKey</code> to connect Amplify CLI with your newly created IAM user.</p><p>Enter the access key of the newly created user: (You have it on the last step of &#xA0;IAM creation step wizard)</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-02-a--21.11.56.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="839" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-02-a--21.11.56.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-02-a--21.11.56.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-02-a--21.11.56.png 1600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-02-a--21.11.56.png 2326w" sizes="(min-width: 720px) 720px"></figure><ul><li><code>? accessKeyId: &#xA0;# YOUR_ACCESS_KEY_ID</code></li><li><code>? secretAccessKey: &#xA0;# YOUR_SECRET_ACCESS_KEY</code></li><li><code>? Profile Name: &#xA0;# (default) &lt;== YOU CAN USE THE DEFAULT PROFLE HERE</code></li></ul><p>You will see the message in the console <code>Successfully set up the new user</code> , you are good to go. &#xA0;Next we&apos;ll set up the app and initialize Amplify!</p><h2 id="2-setup-the-nextjs-project">2. Setup the Next.js project </h2><p>For this project, we will use <code>Yarn</code> as node package manager.</p><p>On the CLI, run the following to initialize an empty next.js project with typescript language <code>yarn create next-app foxnews --typescript &amp;&amp; cd foxnews</code></p><p>Now we have a basic Next.js project. run <code>yarn dev</code> and it will display the default welcome page of Next.js app.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.06.01.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="1356" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--23.06.01.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--23.06.01.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--23.06.01.png 1600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.06.01.png 2322w" sizes="(min-width: 720px) 720px"></figure><p>Create a git repository <a href="https://github.com/new">here</a> &#xA0;and link to the newly created project. If you don&apos;t have a github account, sign up <a href="https://github.com/join">here</a> </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--21.58.06.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="897" height="419" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--21.58.06.png 600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--21.58.06.png 897w" sizes="(min-width: 720px) 720px"></figure><p>You can find your repository URL here. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--22.05.30.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="1731" height="579" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--22.05.30.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--22.05.30.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--22.05.30.png 1600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--22.05.30.png 1731w" sizes="(min-width: 720px) 720px"></figure><p>Now initialize git on the project we just created and link to the remote project with the following:</p><pre><code>git init
git remote add origin https://github.com/Doha26/Foxnews-nextjs-aws.git // REPLACE WITH YOUR REPO URL 
git add .
git commit -m &#x2018;first commit&#x2019;
git branch -M main
git push -u origin main</code></pre><p>Good, the project is ready for version control. Let&apos;s setup Amplify now !</p><h2 id="3-configure-amplify-with-the-project">3. Configure Amplify with the project</h2><p>On the command line, run <code>amplify init</code> to register your project with the amplify framework. It will ask you some questions: </p><ul><li><code>? Enter a name for the project</code> your project name</li><li><code>? Initialize the project with the above configuration?</code> Yes</li><li><code>? Select the authentication method you want to use</code> &#xA0;AWS profile </li><li><code>? Please choose the profile you want to use</code> &#xA0;default </li></ul><p>Lets amplify create everything and when everything will be set, you will see the following output </p><pre><code class="language-Javascript">&#x2714; Successfully created initial AWS cloud resources for deployments.
&#x2714; Initialized provider successfully.
&#x2705; Initialized your environment successfully.

Your project has been successfully initialized and connected to the cloud!</code></pre><p>At this step, our project is ready with Amplify framework. Let&apos;s deploy this basic setup on AWS as Amplify now support SSR web apps. &#xA0;</p><h2 id="4-deploy">4. Deploy &#xA0;</h2><p>For this step, go to the <a href="https://us-west-2.console.aws.amazon.com/amplify/home?region=us-west-2#/">Amplify console</a> &#xFE0F;and follow the steps bellow to connect amplify to your github repo.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.39.51.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="1077" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--23.39.51.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--23.39.51.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--23.39.51.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-03-a--23.39.51.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>On the <strong>Next</strong> screen, choose your repo and select the branch to deploy. In our case, we want to deploy the <code>main</code> branch. &#xA0;</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.28.44.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="1171" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--23.28.44.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--23.28.44.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--23.28.44.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-03-a--23.28.44.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>On the Next screen, Amplify will automatically detect that we are trying to host a Next.Js App with SSR and build settings will auto-populate. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.29.18-1.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="1266" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--23.29.18-1.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--23.29.18-1.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--23.29.18-1.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-03-a--23.29.18-1.png 2400w" sizes="(min-width: 720px) 720px"></figure><ul><li>Check <code>Enable full-stack continuous deployments (CI/CD)</code> </li><li>On <code>Environment</code> dropdown , leave it empty and amplify will use the default branch <code>main</code> for now.</li><li>On select existing services. Click on <code>Create new role</code> this will open a new window and ask you to create a new user</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.31.50.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="1402" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--23.31.50.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--23.31.50.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--23.31.50.png 1600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.31.50.png 2334w" sizes="(min-width: 720px) 720px"></figure><p>On this screen, make sure <strong>Amplify</strong> service is selected and click &#xA0;<code>Next:Permissions</code> </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.32.10.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="581" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--23.32.10.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--23.32.10.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--23.32.10.png 1600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.32.10.png 2284w" sizes="(min-width: 720px) 720px"></figure><p>Here, click <strong>Next</strong></p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.33.07.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="1491" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--23.33.07.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--23.33.07.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--23.33.07.png 1600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.33.07.png 2324w" sizes="(min-width: 720px) 720px"></figure><p>Click <strong>Create role</strong></p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-03-a--23.35.21-1.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="700" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-03-a--23.35.21-1.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-03-a--23.35.21-1.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-03-a--23.35.21-1.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-03-a--23.35.21-1.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Here is the newly created user role. Select this role for <strong>Configure build settings </strong>and you click <strong>Continue</strong>. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-04-a--02.14.18.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="419" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-04-a--02.14.18.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-04-a--02.14.18.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-04-a--02.14.18.png 1600w, https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-04-a--02.14.18.png 2092w" sizes="(min-width: 720px) 720px"></figure><p>On the last screen, &#xA0;you can <strong>Review</strong> your settings and YEAH. Your app is ready for deploy. On this screen, click <strong>Save and Deploy</strong>. &#xA0;AWS will complete the following steps during few minutes and then deploy your Next.js app on a subdomain <code>main.app_id.amplifyapp.com</code></p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-04-a--02.17.29-2.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="1022" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-04-a--02.17.29-2.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-04-a--02.17.29-2.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-04-a--02.17.29-2.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-04-a--02.17.29-2.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Behind the scenes, Amplify creates AWS resources used to deploy your app -- first an <a href="https://aws.amazon.com/s3/pricing/">Amazon S3 bucket</a> to store your app&apos;s static assets, then an <a href="https://aws.amazon.com/cloudfront/pricing/">Amazon CloudFront</a> to serve your app itself, finally a <a href="https://aws.amazon.com/lambda/pricing/">Lambda@Edge function</a> to SSR pages.</p><p>Click on the preview link , you will see the welcome page of the next.js app. Our App is ready on Amplify &#x1F389;</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/06/Capture-d-e-cran-2022-06-04-a--02.27.22.png" class="kg-image" alt="Build and deploy a Fullstack App with Typescript, Next.js, GraphQL, Tailwind CSS 
and AWS - Part 1" loading="lazy" width="2000" height="1023" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/06/Capture-d-e-cran-2022-06-04-a--02.27.22.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/06/Capture-d-e-cran-2022-06-04-a--02.27.22.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/06/Capture-d-e-cran-2022-06-04-a--02.27.22.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/06/Capture-d-e-cran-2022-06-04-a--02.27.22.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>We have completed this First Part of the tutorial. In the next tutorial, we will build the UI Layer of the app to fetch and display news. </p><p>Thanks for reading. The source code is available <a href="https://github.com/Doha26/Foxnews-nextjs-aws">here</a></p><p><em>Follow me on socials media to stay up to date with latest posts.</em></p>]]></content:encoded></item><item><title><![CDATA[Track errors and Monitor your GraphQL backend with sentry SDK.]]></title><description><![CDATA[<p>Hey guys &#x1F91A;, Glad to have you here! In this post we are going to cover something different. Once we have an app running, it&apos;s VERY IMPORTANT to see how our app behave for end users and ensure that everything works as expected. to do so, we need</p>]]></description><link>https://blog.foujeupavel.com/track-error-on-your-graphql-backend-with-sentry-sdk/</link><guid isPermaLink="false">6286a5c9a72f86064975bf5a</guid><category><![CDATA[Backend]]></category><category><![CDATA[Graphql]]></category><category><![CDATA[Typescript]]></category><category><![CDATA[Usefull]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Tue, 31 May 2022 17:20:07 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1589149098258-3e9102cd63d3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDc4fHxjb2RlfGVufDB8fHx8MTY1Mjk5MTgyNg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1589149098258-3e9102cd63d3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDc4fHxjb2RlfGVufDB8fHx8MTY1Mjk5MTgyNg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Track errors and Monitor your GraphQL backend with sentry SDK."><p>Hey guys &#x1F91A;, Glad to have you here! In this post we are going to cover something different. Once we have an app running, it&apos;s VERY IMPORTANT to see how our app behave for end users and ensure that everything works as expected. to do so, we need to monitor performances , track bugs and fix them as soon as they arrive. One of the most powerful SDK to do this is <strong>Sentry. </strong>In this tutorial, we will cover how to configure our GraphQL Backend with. </p><h2 id="1-what-is-sentry-%E2%98%9D%EF%B8%8F">1. What is sentry &#x261D;&#xFE0F; ?</h2><p><a href="https://sentry.io/">Sentry.io</a> is an external monitoring and logging platform as a service which can help you identify and triage errors API&apos;s and Services, webapps and mobile and desktop applications. This makes it an essential and versatile tool for any serious business or developer. </p><p>To have a quick overview of how sentry help you track errors, you can take a look at the sentry demo <a href="https://try.sentry-demo.com/organizations/one-polliwog/issues/?query=is%3Aunresolved">sandbox</a> that show you something like this. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--11.50.26.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="2000" height="982" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--11.50.26.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--11.50.26.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--11.50.26.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/05/Capture-d-e-cran-2022-05-28-a--11.50.26.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>In this tutorial, we will setup sentry on a GraphQL API running on Apollo Server 3. Wether you are building a REST API or a GraphQL Backend, this tutorial will also working for you. </p><h2 id="2-setup-%F0%9F%9B%A0">2. Setup &#x1F6E0;</h2><p>For this implementation, we will use a ready to use node.js graphql server we built on <a href="https://blog.foujeupavel.com/setup-a-node-js-graphql-backend-with-apollo/">another post</a> using Typescript. So we will fetch the source code that run a graphQL server with mocked data and JWT configured for authorization.</p><p>Since the directory containing the initial setup is part of a repository that host multiples tutorials source code each on a single folder, we will only clone the one with the initial setup we need to get start. Follow the folling steps from the terminal.</p><pre><code>git clone --no-checkout https://github.com/Doha26/fullstack-hebdo-posts.git

cd fullstack-hebdo-posts

git sparse-checkout init --cone

git sparse-checkout set  05-nodejs-graphql-sentry
</code></pre><p>And now inside the folder, install node dependencies and start the server</p><pre><code> cd 05-nodejs-graphql-sentry &amp;&amp; yarn &amp;&amp; yarn start:dev</code></pre><p>you will see the following output</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-04-a--00.27.54.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy"></figure><p>This display the apollo server v3 landing page.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--12.14.41.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="1994" height="1524" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--12.14.41.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--12.14.41.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--12.14.41.png 1600w, https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--12.14.41.png 1994w" sizes="(min-width: 720px) 720px"></figure><h2 id="2-lets-setup-a-sentry-project%F0%9F%92%BB">2. Let&apos;s setup a sentry project&#x1F4BB;</h2><p>First of all, go to <a href="https://sentry.io/welcome/">sentry.io</a> and register. After registration, you will see the following onboarding page. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--14.15.11.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="2000" height="1100" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--14.15.11.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--14.15.11.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--14.15.11.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/05/Capture-d-e-cran-2022-05-28-a--14.15.11.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Choose Install Sentry and click on the <strong>Start </strong>Button. The next page will ask you to choose the platform you are setting sentry for. In our case, we want to set this for a Node.JS GraphQL Backend. So we will choose Node.JS to create a Node.Js project on sentry platform. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--14.21.41.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="2000" height="1462" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--14.21.41.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--14.21.41.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--14.21.41.png 1600w, https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--14.21.41.png 2236w" sizes="(min-width: 720px) 720px"></figure><p>The next step will display the following page that help you configure sentry for your Node.Js project. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--14.47.36.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="2000" height="1320" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--14.47.36.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--14.47.36.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--14.47.36.png 1600w, https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--14.47.36.png 2148w" sizes="(min-width: 720px) 720px"></figure><p>In this screenshot, two elements are very important: </p><ul><li>The <strong>DSN</strong>: This stand for Data Source Name. According to sentry documentation, A DSN tells a Sentry SDK where to send events so the events are associated with the correct project. The DSN configures the protocol, public key, server address, and project identifier. It is composed of the following parts:</li></ul><p><code>{PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}{PATH}/{PROJECT_ID}</code></p><p>Note: &#xA0;The <strong>DSN</strong> <strong><strong>should be treated as a secret and not shared with anyone</strong>. </strong>(We will place it inside a .env file)</p><ul><li>The <strong>TracesSampleRate</strong>: determines the percent of events the monitor should send to the dashboard. A value of <code>1.0</code> sends 100% of the captured events &#x2013; but if you find this to be too noisy you can reduce this number. </li></ul><p>Next, you will see the sentry dashboard waiting for event and ready to catch errors.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--16.18.06.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="2000" height="856" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--16.18.06.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--16.18.06.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--16.18.06.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/05/Capture-d-e-cran-2022-05-28-a--16.18.06.png 2400w" sizes="(min-width: 720px) 720px"></figure><h2 id="3-lets-install-%F0%9F%AA%84">3. Let&apos;s install &#xA0;&#x1FA84;</h2><pre><code class="language-javascript"># Using yarn
yarn add @sentry/node @sentry/tracing @sentry/integrations

# Using npm
npm install --save @sentry/node @sentry/tracing @sentry/integrations</code></pre><p>For this tutorial, we will create some environment variable inside a <code>.env</code> file containing <code>JWT_USER_SECRET</code> usefull for the <a href="https://blog.foujeupavel.com/handling-authorization-on-your-graphql-server-with-auth0/">previous</a> blog post on how to <a href="https://blog.foujeupavel.com/handling-authorization-on-your-graphql-server-with-auth0/">Secure a GraphQL backend with JWT</a>. &#xA0;</p><pre><code>JWT_USER_SECRET=ksjsjshbsid_fake_JWT_Token_Secret_Key_key_Here
APP_ENV=development
APP_NAME=Your_app_name_here
APP_REVISION=0.0.1
SENTRY_DSN=Your_DSN_here
</code></pre><p>Create a file under <code>src/utils</code> named <code>sentry.ts</code> or whatever you want that will host our sentry logic. </p><pre><code class="language-javascript">import * as Sentry from &apos;@sentry/node&apos;
import * as Tracing from &apos;@sentry/tracing&apos;
import * as core from &apos;express-serve-static-core&apos;

import {
  RewriteFrames as RewriteFramesIntegration,
  ReportingObserver as ReportingObserverIntegration,
  ExtraErrorData as ExtraErrorDataIntegration,
  CaptureConsole as CaptureConsoleIntegration,
  Dedupe as DedupeIntegration
} from &apos;@sentry/integrations&apos;

const InitSentry = (app: core.Express): void =&gt; {
  Sentry.init({
    environment: process.env.APP_ENV,
    release: `${process.env.APP_NAME}-${process.env.APP_REVISION}` || &apos;0.0.1&apos;,
    dsn: process.env.SENTRY_DSN,
    integrations: [
      new RewriteFramesIntegration({
        root: process.cwd()
      }) as RewriteFramesIntegration,
      new ExtraErrorDataIntegration({
        depth: 10
      }),
      new CaptureConsoleIntegration({
        // array of methods that should be captured
        // defaults to [&apos;log&apos;, &apos;info&apos;, &apos;warn&apos;, StatusError, &apos;debug&apos;, &apos;assert&apos;]
        levels: [&apos;error&apos;, &apos;warn&apos;, &apos;assert&apos;]
      }),
      new Sentry.Integrations.Http({ tracing: true }),
      new Tracing.Integrations.Express({ app }),
      new DedupeIntegration(),
      new ReportingObserverIntegration()
    ],
    tracesSampleRate: 1.0
  })
  app.use(Sentry.Handlers.requestHandler())
  app.use(Sentry.Handlers.tracingHandler())
  app.use(Sentry.Handlers.errorHandler())
}

export default InitSentry
</code></pre><p>And all we have to do is to call the <code>InitSentry()</code> function at server start. Inside <code>src/index.ts</code> file, add the following code : </p><pre><code class="language-javascript">// Bootstrap the server
const bootstrap = async () =&gt; {
  const db = await setupDb()
  const schema = await buildSchema({ resolvers: [EventResolver] })
  const app = express()

  // Init Sentry SDK here 
  InitSentry(app) // ADD THIS LINE &lt;==

  // Express JWT Middleware that parse request and decode token
  app.use(
    expressJwt({</code></pre><p>Everything is now ready. Let&apos;s let&apos;s raise an exception and see how sentry catches it. To raise an exception, let&apos;s &#xA0;add a new Line and see how that will be captured by sentry. &#xA0;</p><pre><code class="language-javascript">import * as Sentry from &apos;@sentry/node&apos; // Import this 

InitSentry(app) // Sentry init function 

Sentry.captureException(new Error(&apos;Testing Sentry SDK&apos;)) // Add this After</code></pre><p>The Error is sent to sentry dashboard</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--17.20.50.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="2000" height="410" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--17.20.50.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--17.20.50.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--17.20.50.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/05/Capture-d-e-cran-2022-05-28-a--17.20.50.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Here is the error stacktrace. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--17.24.49.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="2000" height="981" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--17.24.49.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--17.24.49.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--17.24.49.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/05/Capture-d-e-cran-2022-05-28-a--17.24.49.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Then, anywhere in your code base where you are logging errors (such as a <code>try / catch</code> block or a <code>.catch()</code> chain), add <code>Sentry.captureException(error)</code> (replacing <code>error</code> with the variable that represents your error object) to pass that error off to your Sentry monitor.</p><p>To query the &#xA0;GraphQL server , it&apos;s required to send a JWT Token for queries. to get a JWT token, call the Login Mutation with <code>email</code> and <code>password</code> ( take a look at <code>loadData()</code> function inside <code>src/index.ts</code> to see how it works) and you will get a valid JWT Token that should be send in the header following the format <code>Bearer xxx_token</code>. Take a look at <a href="https://blog.foujeupavel.com/handling-authorization-on-your-graphql-server-with-auth0/">this Post </a>to have more details. </p><p>You can navigate to the <strong>Performance</strong> tab to monitor and measure important metrics such as the FCP (First Contentful Paint), latency or downtime of any API requests, etc.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/05/Capture-d-e-cran-2022-05-28-a--20.59.12.png" class="kg-image" alt="Track errors and Monitor your GraphQL backend with sentry SDK." loading="lazy" width="2000" height="1077" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/05/Capture-d-e-cran-2022-05-28-a--20.59.12.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/05/Capture-d-e-cran-2022-05-28-a--20.59.12.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/05/Capture-d-e-cran-2022-05-28-a--20.59.12.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/05/Capture-d-e-cran-2022-05-28-a--20.59.12.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>And that&apos;s is. Your server is now up and running with Sentry SDK well configured &#x2705;</p><p>Thanks for reading, the source code is available <a href="https://github.com/Doha26/fullstack-hebdo-posts/tree/main/05-nodejs-graphql-sentry">here</a>.</p><p><em>Follow me on socials media to stay up to date with latest posts.</em></p>]]></content:encoded></item><item><title><![CDATA[Improve your Apollo server with Apollo Server Plugins.]]></title><description><![CDATA[<p>As covered in other post on this blog, <a href="https://www.apollographql.com/docs/intro/platform/">Apollo</a> is a platform that allows you to build scalable and robust web/Mobile application by providing you everything you need for client and server when using GraphQL, a query language for data fetching. The image bellow briefly describe it.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/03/apollo.svg" class="kg-image" alt loading="lazy" width="467" height="258"></figure><p>In this</p>]]></description><link>https://blog.foujeupavel.com/improve-your-apollo-server-with-apolloserverplugins/</link><guid isPermaLink="false">621bb994a72f86064975bcff</guid><category><![CDATA[Backend]]></category><category><![CDATA[Best Practices]]></category><category><![CDATA[Graphql]]></category><category><![CDATA[Typescript]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Fri, 04 Mar 2022 01:02:59 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1626624338641-b99e0d32c958?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDMxfHxzZXJ2ZXIlMjB8ZW58MHx8fHwxNjQ2MzU2Mjkx&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1626624338641-b99e0d32c958?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDMxfHxzZXJ2ZXIlMjB8ZW58MHx8fHwxNjQ2MzU2Mjkx&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Improve your Apollo server with Apollo Server Plugins."><p>As covered in other post on this blog, <a href="https://www.apollographql.com/docs/intro/platform/">Apollo</a> is a platform that allows you to build scalable and robust web/Mobile application by providing you everything you need for client and server when using GraphQL, a query language for data fetching. The image bellow briefly describe it.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/03/apollo.svg" class="kg-image" alt="Improve your Apollo server with Apollo Server Plugins." loading="lazy" width="467" height="258"></figure><p>In this blog post, we will work on the server part and will focus how to improve a Node.js GraphQL server by adding some customs features using Plugins.</p><h2 id="1-what-are-plugins">1. What are plugins</h2><p>In computer science and software development, <strong>plugins</strong> are software additions that helps to customize programs, apps, and web browsers. </p><p>For Apollo<strong>, Plugins</strong> extend Apollo Server&apos;s functionality by performing custom operations in response to certain events. These events correspond to individual phases of the GraphQL request lifecycle, and to the lifecycle of Apollo Server itself. </p><p>A basic example can be one that perform an action before server start. To do this, we will override the <code>serverWillStart</code> event.</p><pre><code class="language-javascript">const myPlugin = {
  async serverWillStart() {
    console.log(&apos;Server starting up!&apos;);
  },
};</code></pre><p>when there are well used, plugins can behave like middleware where you perform some actions before or after certain events.</p><p>To understand how plugins works and how we can create customs ones, it is really important to understand GraphQL request lifecycle methods. </p><p>The following diagram illustrates the sequence of events that fire for each request. Each of these events is documented in <a href="https://www.apollographql.com/docs/apollo-server/integrations/plugins-event-reference/">Apollo Server plugin events</a> and available on apollo website.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-03-a--23.39.25.png" class="kg-image" alt="Improve your Apollo server with Apollo Server Plugins." loading="lazy" width="1038" height="1604" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/03/Capture-d-e-cran-2022-03-03-a--23.39.25.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/03/Capture-d-e-cran-2022-03-03-a--23.39.25.png 1000w, https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-03-a--23.39.25.png 1038w" sizes="(min-width: 720px) 720px"></figure><p>So you will be able to create plugins that perform some actions for each of theses specifics operations. To learn more about each of theses methods(), you can read more <a href="The following diagram illustrates the sequence of events that fire for each request. Each of these events is documented in Apollo Server plugin events.">here</a>. </p><h2 id="2-setup">2. Setup</h2><p>For this implementation, we will use a ready to use node.js graphql server we built on <a href="https://blog.foujeupavel.com/setup-a-node-js-graphql-backend-with-apollo/">another post</a>. So we will fetch the source code that run a graphQL server with mocked data and JWT configured for authorization. </p><p>Since the directory containing the initial setup is part of a repository that host multiples tutorials source code each on a single folder, we will only clone the one with the initial setup we need to get start. Follow the folling steps from the terminal.</p><pre><code>git clone --no-checkout https://github.com/Doha26/fullstack-hebdo-posts.git

cd fullstack-hebdo-posts

git sparse-checkout init --cone

git sparse-checkout set  04-nodejs-graphql-apollo-plugins
</code></pre><p>And now inside the folder, install node dependencies and start the server</p><pre><code> cd 04-nodejs-graphql-apollo-plugins &amp;&amp; yarn &amp;&amp; yarn start:dev</code></pre><p>you will see the following output</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-04-a--00.27.54.png" class="kg-image" alt="Improve your Apollo server with Apollo Server Plugins." loading="lazy" width="1688" height="544" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/03/Capture-d-e-cran-2022-03-04-a--00.27.54.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/03/Capture-d-e-cran-2022-03-04-a--00.27.54.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/03/Capture-d-e-cran-2022-03-04-a--00.27.54.png 1600w, https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-04-a--00.27.54.png 1688w" sizes="(min-width: 720px) 720px"></figure><h2 id="3-create-apollo-plugin">3. Create apollo plugin</h2><p>From the terminal, install <code>apollo-server-plugin-base</code> with <code>yarn add apollo-server-plugin-base</code> This lib will give us access to some Object &#xA0;required for graphql server Lifecycle Event methods</p><p>Create a folder called <code>utils</code> inside <code>src</code> folder and add a file called <code>plugin.ts</code></p><p>Inside this file, add the following code to create a Custom Apollo Server plugin. </p><pre><code class="language-javascript">import { GraphQLSchema } from &apos;graphql&apos;
import {
  ApolloServerPlugin,
  BaseContext,
  GraphQLRequestContext,
  GraphQLRequestExecutionListener,
  GraphQLRequestListener,
  GraphQLRequestListenerParsingDidEnd,
  GraphQLRequestListenerValidationDidEnd,
  GraphQLResponse
} from &apos;apollo-server-plugin-base&apos;

export class CustomApolloServerPlugin implements ApolloServerPlugin {
  public serverWillStart(): any {
    console.info(&apos;- Inside serverWillStart&apos;)
  }

  public requestDidStart(): Promise&lt;GraphQLRequestListener&lt;BaseContext&gt;&gt; | any {
    const start = Date.now()
    let variables: any = null
    return {
      didResolveOperation: (): Promise&lt;void&gt; | any =&gt; {
        console.info(&apos;- Inside didResolveOperation&apos;)
      },
      willSendResponse(requestContext: GraphQLRequestContext) {
        const stop = Date.now()
        const elapsed = stop - start
        const size = JSON.stringify(requestContext.response).length * 2
        console.info(
          `operation=${requestContext.operationName},
             duration=${elapsed}ms,
             bytes=${size},
             query=${requestContext.request.query}
             variables:${JSON.stringify(variables)},
             user=${JSON.stringify(requestContext.context.user)}
             responseData=${JSON.stringify(requestContext.response?.data)},
             `
        )
      },
      didEncounterErrors(requestContext: GraphQLRequestContext) {}
    }
  }

  /**
   * Request Lifecycle Handlers
   */

  public parsingDidStart(context: GraphQLRequestContext): Promise&lt;GraphQLRequestListenerParsingDidEnd | void&gt; | any {
    console.info(&apos;- Inside parsingDidStart&apos;, JSON.stringify(context))
  }

  public validationDidStart(
    context: GraphQLRequestContext
  ): Promise&lt;GraphQLRequestListenerValidationDidEnd | void&gt; | any {
    console.info(&apos;- Inside validationDidStart&apos;, JSON.stringify(context))
  }

  public didResolveOperation(context: GraphQLRequestContext): Promise&lt;void&gt; | any {
    console.info(&apos;- Inside didResolveOperation&apos;, JSON.stringify(context))
  }

  public responseForOperation(context: GraphQLRequestContext): GraphQLResponse | any {
    console.info(&apos;- Inside responseForOperation&apos;, JSON.stringify(context))
    return null
  }

  public executionDidStart(context: GraphQLRequestContext): Promise&lt;GraphQLRequestExecutionListener | void&gt; | any {
    console.info(&apos;- Inside executionDidStart&apos;, JSON.stringify(context))
  }

  public didEncounterErrors(context: GraphQLRequestContext): Promise&lt;void&gt; | any {
    console.info(&apos;- Inside didEncounterErrors&apos;, JSON.stringify(context))
  }

  public willSendResponse(context: GraphQLRequestContext): Promise&lt;void&gt; | any {
    console.info(&apos;- Inside willSendResponse&apos;, JSON.stringify(context))
  }
}
</code></pre><p>Once done, open <code>src/index.ts</code> file and add the following to enable the plugin. </p><pre><code class="language-javascript">const server = new ApolloServer({
    schema,
    context: async ({ req, res }): Promise&lt;AppContext | null&gt; =&gt; {
       // ...
    },
    plugins: [new CustomApolloServerPlugin()] // NEW 
  })</code></pre><p>And that&apos;s all. Now if the server is running, you should be able to see the following ouput from the console</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-04-a--01.06.28.png" class="kg-image" alt="Improve your Apollo server with Apollo Server Plugins." loading="lazy" width="1632" height="294" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/03/Capture-d-e-cran-2022-03-04-a--01.06.28.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/03/Capture-d-e-cran-2022-03-04-a--01.06.28.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/03/Capture-d-e-cran-2022-03-04-a--01.06.28.png 1600w, https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-04-a--01.06.28.png 1632w" sizes="(min-width: 720px) 720px"></figure><p>which refer to this in our plugin class </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-04-a--01.07.27.png" class="kg-image" alt="Improve your Apollo server with Apollo Server Plugins." loading="lazy" width="1298" height="204" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/03/Capture-d-e-cran-2022-03-04-a--01.07.27.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/03/Capture-d-e-cran-2022-03-04-a--01.07.27.png 1000w, https://blog.foujeupavel.com/content/images/2022/03/Capture-d-e-cran-2022-03-04-a--01.07.27.png 1298w" sizes="(min-width: 720px) 720px"></figure><h2 id="4-lets-explain-some-of-theses-lifecycle-methods-and-actions-that-can-be-handle-for-each">4. Let&apos;s explain some of theses lifecycle methods and actions that can be handle for each</h2><p>For better understanding on how it can be useful to configure a Custom Apollo plugin, let&apos;s explain (According to apollo) what is behind the scenes of theses methods.</p><ul><li><strong>serverWillStart(): &#xA0;</strong>This method is triggered when te apollo server is ready to start. At this level, it would be interesting to make sure that all your production secret keys (.env VARIABLES ) &#xA0;have been loaded. Otherwise, raise an error</li></ul><p></p><ul><li><strong>requestDidStart()</strong>: Triggered at the beginning of every request cycle, and returns an object (GraphQLRequestListener) that has the functions for each request lifecycle event (didResolveOperation(), willSendResponse(), etc ..)</li></ul><p></p><ul><li><strong>didResolveOperation(</strong>context: <strong>GraphQLRequestContext)</strong>: This event fires after the graphql library successfully determines the operation to execute from a request&apos;s document AST (Abstract Syntax Tree). At this stage, both the operationName string and operation AST are available. the @param context argument expose following items. &#xA0;<code>metrics</code> | <code>source</code> | <code>document</code> | <code>operationName</code> | <code>operation</code></li></ul><p></p><ul><li><strong>willSendResponse</strong>(requestContext: <strong>GraphQLRequestContext</strong>): The &#xA0;event fires whenever Apollo Server is about to send a response for a GraphQL operation. This event fires (and Apollo Server sends a response) even if the GraphQL operation encounters one or more errors. The @param context argument provide following items: <code>metrics</code> | <code>response</code></li></ul><p></p><ul><li><strong>didEncounterErrors</strong>(requestContext: <strong>GraphQLRequestContext</strong>): The didEncounterErrors event fires when Apollo Server encounters errors while parsing, validating, or executing a GraphQL operation. The @param context argument provide following items: <code>metrics</code> | <code>source</code> | <code>errors</code> . Here could be the good place to send the error to your favorite error tracking platform (Bugsnag, sentry, firebase, etc ...). We will see how to do this in another blog post. </li></ul><p></p><ul><li><strong>parsingDidStart</strong>(context: <strong>GraphQLRequestContext</strong>): This event fires whenever Apollo Server will parse a GraphQL request to create its associated document AST (Abstract Syntax Tree). If Apollo Server receives a request with a query string that matches a previous request, the associated document might already be available in Apollo Server&apos;s cache. In this case, parsingDidStart is not called for the request, because parsing does not occur. The @param context argument provide following items: &#xA0;<code>metrics</code> | <code>source</code> </li></ul><p></p><ul><li><strong>validationDidStart</strong>(context: <strong>GraphQLRequestContext</strong> &#xA0;): This event fires whenever Apollo Server will validate a request&apos;s document AST against your GraphQL schema. Like parsingDidStart, this event does not fire if a request&apos;s document is already available in Apollo Server&apos;s cache. (only successfully validated documents are cached by Apollo Server). The document AST is guaranteed to be available at this stage, because parsing must succeed for validation to occur. The @param context argument provide following items: &#xA0;<code>metrics</code> | <code>source</code> | <code>document</code></li></ul><p></p><ul><li><strong>responseForOperation</strong>(context: <strong>GraphQLRequestContext</strong>): This event is fired immediately before GraphQL execution would take place. If its return value resolves to a non-null GraphQLResponse, that result is used instead of executing the query. Hooks from different plugins are invoked in series and the first non-null response is used. The @param context argument provide following items: &#xA0;<code>metrics</code> | <code>source</code> | <code>document</code> | <code>operationName</code> | <code>operation</code></li></ul><p></p><ul><li><strong>executionDidStart</strong>(context: <strong>GraphQLRequestContext</strong>): This event fires whenever Apollo Server begins executing the GraphQL operation specified by a request&apos;s document AST. The @param context argument provide following items: <code>metrics</code> | <code>source</code> | <code>document</code> | <code>operationName</code> | <code>operation</code></li></ul><p>Thanks for reading, the source code is available <a href="https://github.com/Doha26/fullstack-hebdo-posts/tree/main/04-nodejs-graphql-apollo-plugins">here</a>. </p><p><em>Follow me on socials media to stay up to date with latest posts.</em> &#xA0;</p>]]></content:encoded></item><item><title><![CDATA[Secure Access to your Node.js Graphql Server with JWT.]]></title><description><![CDATA[<p>In a <a href="https://blog.foujeupavel.com/setup-a-node-js-graphql-backend-with-apollo/">previous post</a>, we have seen how to create a GraphQL server using <a href="https://nodejs.org/en/">Node.js</a>, <a href="https://www.apollographql.com/">Apollo</a>, and <a href="https://typegraphql.com/">Type-graphql</a>. Now the server is ready to accept queries, we will add a secure layer to avoid our server to handle non-authorized requests. </p><h2 id="1-why-is-it-important-to-secure-%F0%9F%A4%A8">1. Why is it important to secure &#x1F928; ?</h2><p><strong>Servers</strong></p>]]></description><link>https://blog.foujeupavel.com/handling-authorization-on-your-graphql-server-with-auth0/</link><guid isPermaLink="false">6218c6aca72f86064975ba4c</guid><category><![CDATA[APIs]]></category><category><![CDATA[Backend]]></category><category><![CDATA[Graphql]]></category><category><![CDATA[Typescript]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Sun, 27 Feb 2022 17:47:01 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1603899122634-f086ca5f5ddd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHNlY3VyZXxlbnwwfHx8fDE2NDU3OTA5NjE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1603899122634-f086ca5f5ddd?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHNlY3VyZXxlbnwwfHx8fDE2NDU3OTA5NjE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Secure Access to your Node.js Graphql Server with JWT."><p>In a <a href="https://blog.foujeupavel.com/setup-a-node-js-graphql-backend-with-apollo/">previous post</a>, we have seen how to create a GraphQL server using <a href="https://nodejs.org/en/">Node.js</a>, <a href="https://www.apollographql.com/">Apollo</a>, and <a href="https://typegraphql.com/">Type-graphql</a>. Now the server is ready to accept queries, we will add a secure layer to avoid our server to handle non-authorized requests. </p><h2 id="1-why-is-it-important-to-secure-%F0%9F%A4%A8">1. Why is it important to secure &#x1F928; ?</h2><p><strong>Servers</strong> play a vital role in organizations. Their primary function is to provide both data and computational services. Imagine you build and deploy an app that fetches data, private information, and sensitive user information from your server, and everyone can access these informations only once having your server endpoint? it would be catastrophic for your organization because Security vulnerabilities can lead to the loss of critical data or loss of capability and control that can jeopardize the whole organization. So <strong>If you do not secure your servers, then you are treading a dangerous path. </strong></p><h2 id="2-what-is-jwt-%F0%9F%A4%94">2. What is JWT &#x1F914; ?</h2><p>JWT stand for Json Web Token. According to <a href="jwt.io">jwt.io</a> (JWT Official website), JSON Web Token (JWT) is an open standard (<a href="https://tools.ietf.org/html/rfc7519">RFC 7519</a>) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the <strong><strong>HMAC</strong></strong> algorithm) or a public/private key pair using <strong><strong>RSA</strong></strong> or <strong><strong>ECDSA</strong></strong>.</p><p>Although JWTs can be encrypted to also provide secrecy between parties, we will focus on <em>signed</em> tokens. Signed tokens can verify the <em>integrity</em> of the claims contained within it, while encrypted tokens <em>hide</em> those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.</p><p>Here are some scenarios where JSON Web Tokens are useful:</p><ul><li><strong><strong>Authorization</strong></strong>: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.</li><li><strong><strong>Information Exchange</strong></strong>: JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed&#x2014;for example, using public/private key pairs&#x2014;you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn&apos;t been tampered with</li></ul><p>In its compact form, JSON Web Tokens consist of three parts separated by dots (<code>.</code>), which are:</p><ul><li><strong>Header</strong>: Contains information about the token type and the algorithm used to sign the token (for example, HS256)</li><li><strong>Payload</strong>: Contains claims about a particular entity. These statements may have predefined meanings in the JWT specification (known as <em>registered</em> claims) or they can be defined by the JWT user (known as <em>public</em> or <em>private</em> claims)</li><li><strong>Signature</strong>: Helps to verify that no information was changed during the token&#x2019;s transmission by hashing together the token header, its payload, and a secret</li></ul><p>Therefore, a JWT will have the pattern &#xA0;<code><strong>xxxxx.yyyyy.zzzzz</strong></code> and will looks like the following </p><pre><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2Jsb2cuZm91amV1cGF2ZWwuY29tIjp7InJvbGVzIjpbImFkbWluIl0sInBlcm1pc3Npb25zIjpbInJlYWQ6cXVlcnlfc3BlYWNrZXJzIiwicmVhZDpxdWVyeV9zcGVhY2tlciIsInJlYWQ6cXVlcnlfZXZlbnRzIiwicmVhZDpxdWVyeV9ldmVudCIsInJlYWQ6bXV0YXRpb25fbG9naW4iXX0sImlhdCI6MTU4NjkwMDI1MSwiZXhwIjoxNjUxMjc5MTIzLCJzdWIiOiJmYWtlX3N1YmplY3QifQ.nAkH-LoA9g7-iWwJjaV3BpJXG_pB7N6jthhn739INCc</code></pre><p>The header section of the above token would decode to:</p><pre><code class="language-json">{
  &quot;alg&quot;: &quot;HS256&quot;,
  &quot;typ&quot;: &quot;JWT&quot;
}</code></pre><p>And the payload section would decode as follows:</p><pre><code class="language-json">{
  &quot;https://blog.foujeupavel.com&quot;: {
    &quot;roles&quot;: [
      &quot;admin&quot;
    ],
    &quot;permissions&quot;: [
    &quot;read:query_speackers&quot;,
    &quot;read:query_speacker&quot;,
    &quot;read:query_events&quot;,
    &quot;read:query_event&quot;,
    &quot;read:mutation_login&quot;
    ]
  },
  &quot;iat&quot;: 1586900251,
  &quot;exp&quot;: 1651279123,
  &quot;sub&quot;: &quot;fake_subject&quot;
}</code></pre><p>For more information, take a look at <a href="https://jwt.io/introduction/">jwt.io/introduction</a> &#x1F609;</p><h2 id="3-setup">3. Setup</h2><p>In this post, we will implement JWT authorization on a ready-to-use Graphql server we build in my other <a href="https://blog.foujeupavel.com/setup-a-node-js-graphql-backend-with-apollo/">post</a>. The source code is available here on <a href="https://github.com/Doha26/fullstack-hebdo-posts/tree/main/03-nodejs-graphql-jwt">GitHub</a>. Just clone and run it locally to get started. &#xA0;From your favorite terminal, run the following. </p><p><strong>Let&apos;s clone the directory containing the initial setup</strong></p><p>Since the directory containing the initial setup is part of a repository that hosts multiple tutorials source codes each on a single folder, we will only clone the one with the initial setup we need to get started. Follow the following steps from the terminal.</p><pre><code>git clone --no-checkout https://github.com/Doha26/fullstack-hebdo-posts.git

cd fullstack-hebdo-posts

git sparse-checkout init --cone

git sparse-checkout set  03-nodejs-graphql-jwt
</code></pre><p>And now inside the folder, install node dependencies and start the server </p><pre><code> cd 03-nodejs-graphql-jwt &amp;&amp; yarn &amp;&amp; yarn start:dev</code></pre><p>you will see the following output </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-26-a--14.36.09.png" class="kg-image" alt="Secure Access to your Node.js Graphql Server with JWT." loading="lazy" width="1754" height="564" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-26-a--14.36.09.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/02/Capture-d-e-cran-2022-02-26-a--14.36.09.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/02/Capture-d-e-cran-2022-02-26-a--14.36.09.png 1600w, https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-26-a--14.36.09.png 1754w" sizes="(min-width: 720px) 720px"></figure><h2 id="4-lets-implement-jwt">4. Let&apos;s implement JWT.</h2><p>For now, our server just exposes a list of events and speakers of the events and their corresponding locations. Actually, everyone can query the server. Now we will add subscribers and only allow them to query events and speakers. &#xA0; </p><p>A subscriber&apos;s object can have an ID, email, phone number, password, and address. </p><p>So we will have the following: </p><ul><li>A subscriber will log in through a login mutation by providing an email and password</li><li>the server will search among users in a mocked array ready on the server and if there is a user with credentials matching the email/password pair provided by the user, the server will generate a token for the user to query the server and return it back on the login mutation response. </li><li>The user will use this token &#xA0;(if still valid) to query the server and access the list of events and speakers</li></ul><p>open <code>src/types.ts</code> and add the following:</p><pre><code class="language-javascript">@ObjectType({ description: &apos;Subscriber object&apos; })
export class Subscriber {
  @Field() ID: string
  @Field() name: string
  @Field() phoneNumber: string
  @Field() email: string
  @Field() password: string
  @Field(_type =&gt; Location) address: Location
}

@ObjectType({ description: &apos;Login Response object&apos; })
export class LoginResponse {
  @Field() accessToken: string
  constructor(accessToken: string) {
    this.accessToken = accessToken
  }
}</code></pre><p>Inside <code>src/index.ts</code> update <code>loadData()</code> function and add following code to mock Subscribers&apos; data:</p><pre><code class="language-javascript">// Mock subscribers
  const su1 = new Subscriber()
  su1.ID = &apos;7dd8716b-3fea-46f0-8826-264300557ae2&apos;
  su1.address = l1
  su1.name = &apos;Jim Coknag&apos;
  su1.email = &apos;jim.cok@gmail.com&apos;
  su1.password = &apos;texas90&apos;
  su1.phoneNumber = &apos;+33 6077654543&apos;

  const su2 = new Subscriber()
  su2.ID = &apos;1dce60a4-9761-11ec-b909-0242ac120002&apos;
  su2.address = l2
  su2.email = &apos;tom.quirk@outlouk.com&apos;
  su2.name = &apos;Tom QuirkMan&apos;
  su2.password = &apos;axjkjgdtrfd#400&apos;
  su2.phoneNumber = &apos;+1-202-555-0100&apos;

  const su3 = new Subscriber()
  su3.ID = &apos;9118556a-9761-11ec-b909-8842ac120002&apos;
  su3.address = l3
  su3.name = &apos;Pavel Foujeu&apos;
  su3.email = &apos;f.pavel@gmail.com&apos;
  su3.password = &apos;123456FAKE&apos;
  su3.phoneNumber = &apos;+1-202-555-0166&apos;
  
  // ...
  data.subscribers = [su1, su2, su3]</code></pre><p>Inside <code>src/shared.ts</code> add the following variable at the top of the file. This will be the SECRET KEY we will use to sign the JWT token that will be sent to the user. For this post, we will keep it inside the resolver class, but in a real-world scenario, secrets should be kept inside a non-versioned .env file. You can see how to easily manage your .env file in my dedicated post available <a href="https://blog.foujeupavel.com/discover-envalid-a-clean-way-to-access-your-env-file-in-prod/">here</a>. </p><pre><code class="language-javascript">export const jwtSecretKey = &apos;KSSLJFLKNDJBNFJBDHBKDJBKBD&apos; // Fake SECRET KEY. To be stored in .env file
</code></pre><p>Inside &#xA0;<code>src/resolver.ts</code> file, add the following queries for subscribers</p><pre><code class="language-javascript"> @Query(() =&gt; [Subscriber])
  getSubscribers(@Ctx() { db }: AppContext): Subscriber[] | undefined {
    return db.data?.subscribers
  }
  
  @Query(() =&gt; Subscriber)
  getSubscriber(@Arg(&apos;id&apos;, () =&gt; String) id: string, @Ctx() { db }: AppContext): Subscriber | undefined {
    return db.data?.subscribers?.find((subscriber: Subscriber) =&gt; subscriber.ID === id)
  }</code></pre><p>We need to install &#xA0;<code>jsonwebtoken</code> to sign the token. In the terminal, add the following: <code>yarn add jsonwebtoken @types/jsonwebtoken</code></p><pre><code class="language-javascript"> @Mutation(() =&gt; LoginResponse, { nullable: true })
  login(
    @Arg(&apos;email&apos;, () =&gt; String) email: string,
    @Arg(&apos;password&apos;, () =&gt; String) password: string,
    @Ctx() { db }: AppContext
  ): LoginResponse | null {
    const user = db.data?.subscribers?.find(
      (subscriber: Subscriber) =&gt; subscriber.email === email &amp;&amp; subscriber.password === password
    )
    if (user) {
      return new LoginResponse(
        jwt.sign(
          {
            &apos;https://blog.foujeupavel.com&apos;: {
              roles: [&apos;admin&apos;],
              permissions: [
                &apos;read:query_speackers&apos;,
                &apos;read:query_speacker&apos;,
                &apos;read:query_events&apos;,
                &apos;read:query_event&apos;,
                &apos;read:mutation_login&apos;
              ]
            }
          },
          jwtSecretKey,
          { algorithm: &apos;HS256&apos;, subject: user.ID, expiresIn: &apos;1d&apos; }
        )
      )
    }
    return null
  }</code></pre><p>With the server running, open apollo sandbox to test all this. </p><ul><li>Get Subscribers</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.08.png" class="kg-image" alt="Secure Access to your Node.js Graphql Server with JWT." loading="lazy" width="2000" height="755" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.08.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.08.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.08.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.08.png 2400w" sizes="(min-width: 720px) 720px"></figure><ul><li>Login user with email/password</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.38.png" class="kg-image" alt="Secure Access to your Node.js Graphql Server with JWT." loading="lazy" width="2000" height="865" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.38.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.38.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.38.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/02/Capture-d-e-cran-2022-02-27-a--04.11.38.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>We&#x2019;ll paste the returned token into the &#x201C;HTTP Headers&#x201D; panel of GraphQL Playground as follows:</p><pre><code class="language-json">{
  &quot;Authorization&quot;: &quot;Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2Jsb2cuZm91amV1cGF2ZWwuY29tIjp7InJvbGVzIjpbImFkbWluIl0sInBlcm1pc3Npb25zIjpbInJlYWQ6cXVlcnlfc3BlYWNrZXJzIiwicmVhZDpxdWVyeV9zcGVhY2tlciIsInJlYWQ6cXVlcnlfZXZlbnRzIiwicmVhZDpxdWVyeV9ldmVudCIsInJlYWQ6bXV0YXRpb25fbG9naW4iXX0sImlhdCI6MTY0NTkzMDM1MywiZXhwIjoxNjQ2MDE2NzUzLCJzdWIiOiI5MTE4NTU2YS05NzYxLTExZWMtYjkwOS04ODQyYWMxMjAwMDIifQ.tWyHd2xA1LiZPy1Vt-R9l9V8Nb8qh6pM-cXwI-UV2uE&quot;
}</code></pre><p>Next, we&#x2019;ll install Express middleware that verifies and decodes the JWT when it&#x2019;s sent with requests from GraphQL Playground: <code>yarn add express-jwt</code></p><p>Then we&#x2019;ll add the middleware to the <code>index.js</code> file, using the same secret that was used to sign the JWT in the mutation, choosing the same signing algorithm, and setting the <code>credentialsRequired</code> option to <code>false</code> so Express won&#x2019;t throw an error if a JWT hasn&#x2019;t been included (which would be the case for the initial <code>login</code> mutation or when GraphQL Playground polls for schema updates):</p><p>Update <code>src/types.ts</code> and update <code>AppContext</code> type with the following </p><pre><code class="language-javascript">interface JWTClaim { // NEW 
  [key: string]: {
    roles: string[]
    permissions: string[]
  }
}
export type AppContext = {
  req: Request
  res: Response
  db: Low&lt;JsonData&gt;
  user:
    | (JWTClaim &amp; {
        sub: string
        iat: number
        exp: number
      })
    | Express.User
    | null
}</code></pre><p>Update <code>src/index.ts</code> and add the following:</p><pre><code class="language-javascript">//...
import expressJwt from &apos;express-jwt&apos;
import { jwtSecretKey } from &apos;./shared&apos;
import expressJwt, { UnauthorizedError } from &apos;express-jwt&apos;

//... Inside bootstrap() function 

 app.use( // NEW 
    expressJwt({
      secret: jwtSecretKey,
      algorithms: [&apos;HS256&apos;],
      credentialsRequired: false
    })
  )

// ... 

const server = new ApolloServer({
    schema,
    context: async ({ req, res }): Promise&lt;AppContext&gt; =&gt; {
      const user = req.user || null // NEW
    if (!req.body.query.trim().startsWith(&apos;query IntrospectionQuery&apos;)) {
        if (req.body.operationName !== &apos;login&apos;) {
          if (!user)
            throw new UnauthorizedError(&apos;Invalid_token&apos;, {
              message: &apos;Unauthorized : Invalid or Expired JWT Token provided !&apos;
            })
        }
      }
      return {
        req,
        res,
        db,
        user // NEW
      }
    }
  })
</code></pre><p>The middleware added to Express will get the token from the <code>Authorization</code> header, decode it, and add it to the request object as <code>req.user</code>. It&#x2019;s a common practice to add decoded tokens to <a href="https://www.apollographql.com/docs/apollo-server/security/authentication/#putting-user-info-on-the-context">Apollo Server&#x2019;s context</a> because the <code>context</code> object is conveniently available in every resolver and it&#x2019;s recreated with every request. </p><p>To avoid even the login mutation that allows the user to get a JWT, the code below has been added to not throw an error if there is no <code>req.user</code> . This way, the login mutation can be called each time needed with corresponding credentials and return a valid accessToken.</p><pre><code class="language-javascript">if (!req.body.query.trim().startsWith(&apos;query IntrospectionQuery&apos;)) {
        if (req.body.operationName !== &apos;login&apos;) {
          // ...
        }
      }</code></pre><p>Now if you query the server with an INVALID or NO TOKEN in the Header section of the request, you will have the following </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-27-a--17.52.03.png" class="kg-image" alt="Secure Access to your Node.js Graphql Server with JWT." loading="lazy" width="855" height="105" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-27-a--17.52.03.png 600w, https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-27-a--17.52.03.png 855w" sizes="(min-width: 720px) 720px"></figure><p>And if you provide a valid token following the pattern <code>Authorization: Bearer accessToken</code>, you will get a response.</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-27-a--18.31.02.png" class="kg-image" alt="Secure Access to your Node.js Graphql Server with JWT." loading="lazy" width="1345" height="825" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-27-a--18.31.02.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/02/Capture-d-e-cran-2022-02-27-a--18.31.02.png 1000w, https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-27-a--18.31.02.png 1345w" sizes="(min-width: 720px) 720px"></figure><p>And that&apos;s it. Your server is now up and running with JWT well configured &#x2705;</p><p>Thanks for reading, the source code is available <a href="https://github.com/Doha26/fullstack-hebdo-posts/tree/main/03-nodejs-graphql-jwt">here</a>. </p><p><em>Follow me on social media to stay up to date with latest posts.</em> &#xA0;</p>]]></content:encoded></item><item><title><![CDATA[Setup a GraphQL backend with Node.JS, TypeGraphGL and Apollo Server]]></title><description><![CDATA[<h2 id="1-what-is-apollo">1. What is Apollo ?</h2><p>According to their documentation, Apollo is a platform for building a unified <strong>graph</strong>, a communication layer that helps you manage the flow of data between your application clients (such as web and native apps) and your <strong>back-end services</strong>. At the heart of the graph is a</p>]]></description><link>https://blog.foujeupavel.com/setup-a-node-js-graphql-backend-with-apollo/</link><guid isPermaLink="false">61f2e03ba72f86064975b5cf</guid><category><![CDATA[APIs]]></category><category><![CDATA[Backend]]></category><category><![CDATA[Graphql]]></category><category><![CDATA[Typescript]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Sun, 20 Feb 2022 18:18:36 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1614726365930-627c75da663e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGFwb2xsb3xlbnwwfHx8fDE2NDMzMDcwODI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<h2 id="1-what-is-apollo">1. What is Apollo ?</h2><img src="https://images.unsplash.com/photo-1614726365930-627c75da663e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGFwb2xsb3xlbnwwfHx8fDE2NDMzMDcwODI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Setup a GraphQL backend with Node.JS, TypeGraphGL and Apollo Server"><p>According to their documentation, Apollo is a platform for building a unified <strong>graph</strong>, a communication layer that helps you manage the flow of data between your application clients (such as web and native apps) and your <strong>back-end services</strong>. At the heart of the graph is a query language called <strong>GraphQL. </strong></p><p>The platform is Divided into 3 main parts. </p><ul><li><strong>Apollo Clients :</strong> it&apos;s a set of client libraries that allows you to build awesome frontend apps using <strong>React</strong>, <strong>iOS</strong>, <strong>Kotlin</strong> with Apollo. </li><li><strong>Apollo Backend :</strong> &#xA0;It&apos;s Includes a set of tools developed for building backends using <strong>Apollo serve</strong>r, <strong>Apollo Federation</strong>, <strong>Apollo Router. </strong></li><li><strong>Apollo tools :</strong> It&apos;s Includes a set of tools to enhance your GraphQl Backends and monitor your schema metrics. i will write another post to focus on how to use Rover CLI and Apollo Studio.</li></ul><p>Since this post is not focus on presenting the Apollo platform, i will only focus on using GraphQL Backend with Apollo Server. </p><h2 id="2-apollo-server">2. Apollo Server</h2><p><strong>Apollo Server is an <a href="https://github.com/apollographql/apollo-server" rel="noopener noreferrer">open-source</a>, spec-compliant GraphQL server</strong> that&apos;s compatible with any GraphQL client. It&apos;s the best way to build a production-ready, self-documenting GraphQL API that can use data from any source. apollo server can be used as : </p><ul><li>A stand-alone GraphQL server, including in a serverless environment</li><li>An add-on to your application&apos;s existing <a href="https://www.apollographql.com/docs/apollo-server/integrations/middleware/">Node.js middleware</a></li><li>A gateway federated graph</li></ul><p>In this post, we will use Apollo server version <strong>3</strong></p><h2 id="3-typegraphql">3. TypeGraphQL </h2><p><strong>TypeGraphQL</strong> is a set of useful features, like validation, authorization and dependency injection, which helps develop GraphQL APIs quickly &amp; easily! I will Cover Typegraphql features in another post. Basically, it allows your to build a graphQl schema by defining classes and a bit of decorators. We will see how to do that. </p><h2 id="4-setup-%F0%9F%8F%84%F0%9F%8F%BB%E2%80%8D%E2%99%82%EF%B8%8F">4. Setup &#x1F3C4;&#x1F3FB;&#x200D;&#x2642;&#xFE0F;</h2><p>In this post i will use typescript with <a href="https://github.com/stemmlerjs/simple-typescript-starter">this</a> open source &#xA0;node-typescript-starter project for our purposes. </p><ul><li>Open your favorite terminal and clone the starter template with <code>git clone <a href="https://github.com/stemmlerjs/simple-typescript-starter.git">https://github.com/stemmlerjs/simple-typescript starter.git</a> your_project_name</code> &#xA0;and <code>cd your_project_name</code></li><li>Install node dependencies with <code>npm install</code> if you are using <code>npm</code> and <code>yarn add</code> if you are using <code>YARN</code> as a package manager. In this post we will use yarn so you will need to delete the <code>package-lock.json</code> at the root folder to avoid conflict with yarn &apos;s <code>-lock.json</code> file.</li><li>Then in the terminal , <code>npm start</code> You should be able to see the output </li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/01/Capture-d-e-cran-2022-01-27-a--23.07.31.png" class="kg-image" alt="Setup a GraphQL backend with Node.JS, TypeGraphGL and Apollo Server" loading="lazy" width="1194" height="158" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/01/Capture-d-e-cran-2022-01-27-a--23.07.31.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/01/Capture-d-e-cran-2022-01-27-a--23.07.31.png 1000w, https://blog.foujeupavel.com/content/images/2022/01/Capture-d-e-cran-2022-01-27-a--23.07.31.png 1194w" sizes="(min-width: 720px) 720px"></figure><ul><li>install apollo-server-express (we will be using apollo-server with the express package ) and typegraphql with <code>yarn add graphql@15.5.0 express class-validator reflect-metadata type-graphql apollo-server-express</code></li></ul><p>Once done, we are good to go &#x1F468;&#x200D;&#x1F4BB;</p><h2 id="5-todolist-%F0%9F%93%83">5. Todolist &#x1F4C3;</h2><p>In this post, we are going to build a backend that expose a GraphQL API showing a list of &#xA0;<strong>Events </strong>and related informations. Here are some specs</p><ul><li>Each event has a name, a place where it will take place, a number of available places, a list of speakers, a date and a rating given by subscribers.</li><li>Each speacker has a name, a phone number, a mail address, &#xA0;a list of topics he will cover</li><li>A speacker can attend several events (Not at the same time)</li></ul><p>Thus, the API should be able to : </p><ul><li>List all events and give details for each of them (speackers, rating, etc)</li><li>List all speackers </li><li>Show details about a specific Event or speaker. </li></ul><p>To go fast, We are not going to setup a real database but will use <strong><a href="https://github.com/typicode/lowdb">LowDB</a>, </strong>a<strong> </strong>tiny in-memory database<strong> </strong>with typescript support<strong> </strong>to persist data in a .json file. </p><p>To be simple without making a relational database, we will have the following objects: </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-07-a--04.11.32.png" class="kg-image" alt="Setup a GraphQL backend with Node.JS, TypeGraphGL and Apollo Server" loading="lazy" width="637" height="158" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-07-a--04.11.32.png 600w, https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-07-a--04.11.32.png 637w"></figure><h2 id="6-lets-create-the-server">6. Let&apos;s create the server. </h2><p>create 3 typescript (.ts) files inside the <strong>src</strong> folder :</p><ul><li><strong>index.ts : </strong>this<strong> </strong>will be the entry point of our graphql server where we will bootstrap our server</li><li><strong>types.ts : </strong>this file will contain all our typegraphql class that will be serialized and stored &#xA0;as json objects.</li><li><strong>resolver.ts</strong> : This file will be our resolver where we will define queries</li></ul><h2 id="61-typests-%F0%9F%93%9D">6.1 types.ts &#x1F4DD;</h2><p>Inside this file, add the following : </p><pre><code class="language-javascript">import { Field, ObjectType, Int, registerEnumType } from &apos;type-graphql&apos;

export enum EventStatus {
  PASSED,
  PENDING,
  UPCOMMING
}

registerEnumType(EventStatus, {
  name: &apos;EventStatus&apos;,
  description: &apos;EventStatus type&apos;
})

@ObjectType({ description: &apos;Location object&apos; })
export class Location {
  @Field() ID: string
  @Field() city: string
  @Field() country: string
  @Field() postalCode: number
}

@ObjectType({ description: &apos;Speaker object&apos; })
export class Speaker {
  @Field() ID: string
  @Field() name: string
  @Field() phoneNumber: string
  @Field() email: string
  @Field(_type =&gt; [String]) topics: Array&lt;string&gt;
  @Field(_type =&gt; [String]) events: Array&lt;string&gt;
}

@ObjectType({ description: &apos;Subscriber object&apos; })
export class Subscriber {
  @Field() ID: string
  @Field() name: string
  @Field() phoneNumber: string
  @Field() email: string
  @Field(_type =&gt; Location) address: Location
}

@ObjectType({ description: &apos;Event object&apos; })
export class Event {
  @Field() ID: string
  @Field() name: string
  @Field(_type =&gt; Location) address: Location
  @Field(_type =&gt; EventStatus) status: EventStatus
  @Field(_type =&gt; Int) availableplace: number
  @Field(_type =&gt; Int) rating: number
  @Field(_type =&gt; [Speaker]) speakers: Array&lt;Speaker&gt;
}
</code></pre><h2 id="62-resolverts-%F0%9F%93%9D">6.2 resolver.ts &#x1F4DD;</h2><p>To get started, we will add a basic query to retrieve empty events array . to do so, add the following inside. </p><pre><code class="language-javascript">import { Query, Resolver } from &apos;type-graphql&apos;
import { Event } from &apos;./types&apos;

@Resolver()
export default class EventResolver {
  @Query(() =&gt; [Event])
  getEvents(): Event[] {
    return []
  }
}
</code></pre><p>Let&apos;s give some explanations.</p><p>Typegraphql use <code>decorators</code> for class and field properties to perform some actions. &#xA0;Everything about typegraphql will be covered in another blog post. &#xA0;for the previous bloc, we have: </p><ul><li><code>@Resolver()</code> : this &#xA0;decorator make a class that will behave like a controller from classic REST frameworks</li><li><code>@Query()</code> : this decorator above a function specify that the function is a Query typegraphql also provide <code>@Mutation()</code> for mutations</li><li> <code>@Subscription()</code> for subscriptions. A decorator can take an argument where to specify the return type of the function. For <code>getEvents()</code> , the function will return an array of <code>Event</code></li></ul><h2 id="63-indexts-%F0%9F%93%9D">6.3 index.ts &#xA0;&#x1F4DD;</h2><pre><code class="language-javascript">import &apos;reflect-metadata&apos; // Important
import express from &apos;express&apos;
import { ApolloServer } from &apos;apollo-server-express&apos;
import EventResolver from &apos;./resolver&apos;
import { buildSchema } from &apos;type-graphql&apos;

// Set a default port to 4000
const PORT = process.env.PORT || 4000

// Bootstrap the server
const bootstrap = async () =&gt; {
  const schema = await buildSchema({ resolvers: [EventResolver] })
  const app = express()
  const server = new ApolloServer({ schema })
  await server.start()
  server.applyMiddleware({ app })
  app.listen(PORT, () =&gt; console.log(`&#x1F680; GraphQL server ready at http://localhost:${PORT}`))
}
bootstrap().catch(error =&gt; console.log(error))

</code></pre><p>And at this point, everything is ready to start the server with <code>yarn start:dev</code> inside the terminal. We will have the following output. &#xA0;</p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-05-a--20.18.24.png" class="kg-image" alt="Setup a GraphQL backend with Node.JS, TypeGraphGL and Apollo Server" loading="lazy" width="753" height="62" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-05-a--20.18.24.png 600w, https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-05-a--20.18.24.png 753w" sizes="(min-width: 720px) 720px"></figure><p>This means our server is ready and will accept request coming from the default port. To be sure our server works well, &#xA0;open <code><a href="http://localhost:4000">http://localhost:400</a>0/graphql</code> &#xA0;in the browser, and you will be be able to see the apollo&apos;s v3 default landing page. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-05-a--20.21.35.png" class="kg-image" alt="Setup a GraphQL backend with Node.JS, TypeGraphGL and Apollo Server" loading="lazy" width="988" height="1280" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-05-a--20.21.35.png 600w, https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-05-a--20.21.35.png 988w" sizes="(min-width: 720px) 720px"></figure><p>We&apos;re up and running!</p><p>Hit the &quot;Query your server&quot; button and you will be redirected to Apollo studio sandbox. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-05-a--20.25.48.png" class="kg-image" alt="Setup a GraphQL backend with Node.JS, TypeGraphGL and Apollo Server" loading="lazy" width="2000" height="1216" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-05-a--20.25.48.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/02/Capture-d-e-cran-2022-02-05-a--20.25.48.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/02/Capture-d-e-cran-2022-02-05-a--20.25.48.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/02/Capture-d-e-cran-2022-02-05-a--20.25.48.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>As we now have a single query named <strong>getEvents()</strong>, It will be listed as in the screen. You can play with the sand box and see how it works. &#xA0;Since Apollo Studio is a great tools to manage graphql schema, i will cover it in another post. </p><h2 id="7-setup-lowdb">7. Setup LowDB</h2><p>As said previously, <strong>lowdb</strong> is a widely used json database. The library is &#xA0;a pure ESM package, so cannot be imported with our current project setup which output <strong>commonjs</strong>. To configure our project to use lowdb, we can update our project configs to output ESM with following. </p><ul><li>Install lowdb with <code>yarn add lowdb</code></li><li>Add following lines inside &#xA0;<code>tsconfig.json</code> file &#xA0;in <code>compilerOptions</code></li></ul><pre><code class="language-javascript">&quot;module&quot;: &quot;ES2020&quot;,
&quot;moduleResolution&quot;: &quot;node&quot;,</code></pre><ul><li>Add following inside <code>package.json</code> file </li></ul><pre><code class="language-javascript">  &quot;type&quot;: &quot;module&quot;,</code></pre><ul><li>Update <code>ts-node</code> package and install <code>10.x.x</code> version that have &#xA0;<strong>esm </strong>support. &#xA0;In my case i have &#xA0;<code>ts-node v10.4.0</code></li><li>Update the exec script inside nodemon.json file at the root with <code>node --loader ts-node/esm --experimental-specifier-resolution=node ./src/index.ts</code></li><li>Restart the server with <code>yarn start:dev</code>	everything should now works fine. </li></ul><h2 id="8-update-server">8. Update server</h2><p>At this step, our server is ready to work with lowDb. Now we will setup our JSON database with some mocked data. </p><ul><li>Update <code>index.ts</code> file and ad the following code </li></ul><pre><code class="language-javascript">import &apos;reflect-metadata&apos; // Important
import express from &apos;express&apos;
import { Low, JSONFile } from &apos;lowdb&apos;
import { ApolloServer } from &apos;apollo-server-express&apos;
import EventResolver from &apos;./resolver&apos;
import { buildSchema } from &apos;type-graphql&apos;
import { AppContext, Event, EventStatus, JsonData, Location, Speaker } from &apos;./types&apos;

// Set a default port to 4000
const PORT = process.env.PORT || 4000

const loadData = (): JsonData =&gt; {
  const data: JsonData = {}

  // Mock locations
  const l1 = new Location()
  l1.ID = &apos;9f9471dc-87bc-11ec-a8a3-0242ac120002&apos;
  l1.country = &apos;England&apos;
  l1.city = &apos;London&apos;
  l1.postalCode = 5208

  const l2 = new Location()
  l2.ID = &apos;e3098c76-87bd-11ec-a8a3-0242ac120002&apos;
  l2.country = &apos;France&apos;
  l2.city = &apos;Paris&apos;
  l2.postalCode = 75000

  const l3 = new Location()
  l3.ID = &apos;db6c8478-87bd-11ec-a8a3-0242ac120002&apos;
  l3.country = &apos;US&apos;
  l3.city = &apos;San Francisco, CA&apos;
  l3.postalCode = 94016

  const l4 = new Location()
  l4.ID = &apos;e8e94ae6-87bd-11ec-a8a3-0242ac120002&apos;
  l4.country = &apos;Spain&apos;
  l4.city = &apos;Barcelona&apos;
  l4.postalCode = 8001

  // Mock speakers
  const s1 = new Speaker()
  s1.ID = &apos;da554858-87be-11ec-a8a3-0242ac120002&apos;
  s1.name = &apos;Tom kirke&apos;
  s1.phoneNumber = &apos;+44 2045772856&apos;
  s1.email = &apos;kirke.tom@gmail.com&apos;
  s1.address = l1
  s1.topics = [&apos;GraphQL&apos;, &apos;Docker&apos;, &apos;Typescript&apos;, &apos;React&apos;, &apos;Jamstack&apos;]

  const s2 = new Speaker()
  s2.ID = &apos;da553d18-87be-11ec-a8a3-0242ac120002&apos;
  s2.name = &apos;Manu Kholns&apos;
  s2.phoneNumber = &apos;+33 678554412&apos;
  s2.email = &apos;k.manu06@outlook.com&apos;
  s2.address = l2
  s2.topics = [&apos;Node&apos;, &apos;Docker&apos;, &apos;Python&apos;, &apos;CI/CD&apos;, &apos;REST&apos;]

  const s3 = new Speaker()
  s3.ID = &apos;da553c0a-87be-11ec-a8a3-0242ac120002&apos;
  s3.name = &apos;Adams Renols&apos;
  s3.phoneNumber = &apos;+14844608120&apos;
  s3.email = &apos;renols.adams@gmail.com&apos;
  s3.address = l3
  s3.topics = [&apos;Docker&apos;, &apos;Kubernetes&apos;, &apos;G-Cloud&apos;, &apos;AWS&apos;, &apos;Devops&apos;]

  const s4 = new Speaker()
  s4.ID = &apos;da55387c-87be-11ec-a8a3-0242ac120002&apos;
  s4.name = &apos;Franck Zein&apos;
  s4.phoneNumber = &apos;+33 687445512&apos;
  s4.email = &apos;franck.z@gmail.com&apos;
  s4.address = l4
  s4.topics = [&apos;React&apos;, &apos;Angular&apos;, &apos;Python&apos;, &apos;AWS&apos;]

  //Mock events
  const e1 = new Event()
  e1.ID = &apos;7b9b0eb2-87bc-11ec-a8a3-0242ac120002&apos;
  e1.name = &apos;GraphQL Submit 2022&apos;
  e1.date = &apos;2022-03-07T03:52:44+01:00&apos;
  e1.status = EventStatus.UPCOMING
  e1.availableplaces = 310
  e1.rating = 4
  e1.address = l1
  e1.speakers = [s1, s2]

  const e2 = new Event()
  e2.ID = &apos;7b9b110a-87bc-11ec-a8a3-0242ac120002&apos;
  e2.name = &apos;Over React-ed 2021&apos;
  e2.date = &apos;2021-11-12T11:30:44+01:00&apos;
  e1.availableplaces = 270
  e2.status = EventStatus.PASSED
  e2.rating = 4
  e2.address = l2
  e2.speakers = [s1, s2, s3]

  const e3 = new Event()
  e3.ID = &apos;7b9b14ca-87bc-11ec-a8a3-0242ac120002&apos;
  e3.name = &apos;Devops Next Gen&apos;
  e3.date = &apos;2022-05-12T11:30:44+01:00&apos;
  e1.availableplaces = 412
  e3.status = EventStatus.UPCOMING
  e3.rating = 4
  e3.address = l4
  e3.speakers = [s1, s2, s3]

  //Create DB Data
  data.locations = [l1, l2, l3, l4]
  data.speakers = [s1, s2, s3, s4]
  data.events = [e1, e2, e3]

  return data
}
const setupDb = async (): Promise&lt;Low&lt;JsonData&gt;&gt; =&gt; {
  const adapter = new JSONFile&lt;JsonData&gt;(&apos;db.json&apos;)
  const db = new Low(adapter)
  await db.read()
  db.data = loadData()
  await db.write()
  return db
}

// Bootstrap the server
const bootstrap = async () =&gt; {
  const db = await setupDb()
  const schema = await buildSchema({ resolvers: [EventResolver] })
  const app = express()
  const server = new ApolloServer({
    schema,
    context: async ({ req, res }): Promise&lt;AppContext&gt; =&gt; {
      return {
        req,
        res,
        db
      }
    }
  })
  await server.start()
  server.applyMiddleware({ app })
  app.listen(PORT, () =&gt; console.log(`&#x1F680; GraphQL server ready at http://localhost:${PORT}/graphql`))
}
bootstrap().catch(error =&gt; console.log(error))
</code></pre><p>Let&apos;s explain theses updates. we added two functions:</p><ul><li> <code>loadData()</code> which is a function that mock some demo data for locations, speakers, and events. </li><li><code>setupDb()</code> which setup lowDb and load mocked datas into it. Thus, we will be able to query theses data in our resolvers functions</li></ul><p>Add two custom types inside <code>type.ts</code> file </p><pre><code class="language-javascript">export type JsonData = {
  events?: Event[]
  speakers?: Speaker[]
  subscribers?: Subscriber[]
  locations?: Location[]
}

export type AppContext = {
  req: Request
  res: Response
  db: Low&lt;JsonData&gt;
}</code></pre><ul><li>Inside <code>resolver.ts</code> add resolver functions to get events and speackers</li></ul><pre><code class="language-javascript">import { Arg, Ctx, Query, Resolver } from &apos;type-graphql&apos;
import { AppContext, Event, Speaker } from &apos;./types&apos;

@Resolver()
export default class EventResolver {
  @Query(() =&gt; [Event])
  getEvents(@Ctx() { db }: AppContext): Event[] | undefined {
    return db.data?.events
  }

  @Query(() =&gt; Event)
  getEvent(@Arg(&apos;id&apos;, () =&gt; String) id: string, @Ctx() { db }: AppContext): Event | undefined {
    return db.data?.events?.find((event: Event) =&gt; event.ID === id)
  }

  @Query(() =&gt; [Speaker])
  getSpeakers(@Ctx() { db }: AppContext): Speaker[] | undefined {
    return db.data?.speakers
  }

  @Query(() =&gt; Event)
  getSpeaker(@Arg(&apos;id&apos;, () =&gt; String) id: string, @Ctx() { db }: AppContext): Speaker | undefined {
    return db.data?.speakers?.find((speaker: Speaker) =&gt; speaker.ID === id)
  }
}
</code></pre><p>And here, everything is ready. But before querying our server, let&#x2019;s &#xA0;explain what one of theses functions does. <code>getEvents()</code> function for example. </p><pre><code class="language-javascript">@Query(() =&gt; [Event])
  getEvents(@Ctx() { db }: AppContext): Event[] | undefined {
    return db.data?.events
  }</code></pre><p>There is not a lot to say here, we just passed the db instance through the context and got it using <code>@Ctx()</code> decorator. And next, we just return all events. &#xA0;</p><h2 id="9-lets-test-it-all">9. let&apos;s test it all. </h2><p>Open apollo studio sandbox at <code>localhost:4000/graphql</code> and query the server. </p><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/02/Capture-d-e-cran-2022-02-20-a--00.24.50.png" class="kg-image" alt="Setup a GraphQL backend with Node.JS, TypeGraphGL and Apollo Server" loading="lazy" width="2000" height="967" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/02/Capture-d-e-cran-2022-02-20-a--00.24.50.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/02/Capture-d-e-cran-2022-02-20-a--00.24.50.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/02/Capture-d-e-cran-2022-02-20-a--00.24.50.png 1600w, https://blog.foujeupavel.com/content/images/size/w2400/2022/02/Capture-d-e-cran-2022-02-20-a--00.24.50.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>and here we have the result on this screen. </p><ul><li>section 1 : this bloc display all queries, mutations, subscriptions you write inside your resolver class with description about each functions, fields and types</li><li>section 2: this section is where you write your request query, mutation or subscription</li><li>section 3: this section will just display the result. </li></ul><p>Thanks for reading, the source code is available <a href="https://github.com/Doha26/fullstack-hebdo-posts/tree/main/02-nodejs-typegraphql-apollo">here</a>. </p><p><em>Follow me on socials media to stay up to date with latest posts.</em> &#xA0; </p>]]></content:encoded></item><item><title><![CDATA[Manage your Node.js .env file with Envalid : A clean way to access your secrets keys in prod ⚡️]]></title><description><![CDATA[<h2 id="1-what-is-a-env-file">1. What is a .env file? </h2><p>A <strong>.env</strong> or <strong>dotenv</strong> file of an app is a text file that contains environment-specific configurations. Most of the time between Local, Staging, and Production environments, your app will need to behave visually the same but behind the scenes, each of these environments can</p>]]></description><link>https://blog.foujeupavel.com/manage-your-node-js-env-file-with-envalid-a-clean-way-to-access-your-secrets-keys-in-prod/</link><guid isPermaLink="false">61ddb879a72f86064975b2e2</guid><category><![CDATA[Usefull]]></category><category><![CDATA[Typescript]]></category><category><![CDATA[Backend]]></category><category><![CDATA[Best Practices]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Fri, 14 Jan 2022 18:39:44 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1603899122406-e7eb957f9fd6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDd8fHNlY3VyZXxlbnwwfHx8fDE2NTQ0NTU3Mzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<h2 id="1-what-is-a-env-file">1. What is a .env file? </h2><img src="https://images.unsplash.com/photo-1603899122406-e7eb957f9fd6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDd8fHNlY3VyZXxlbnwwfHx8fDE2NTQ0NTU3Mzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Manage your Node.js .env file with Envalid : A clean way to access your secrets keys in prod &#x26A1;&#xFE0F;"><p>A <strong>.env</strong> or <strong>dotenv</strong> file of an app is a text file that contains environment-specific configurations. Most of the time between Local, Staging, and Production environments, your app will need to behave visually the same but behind the scenes, each of these environments can be configured with different keys, constants, API Keys, etc. </p><h2 id="2-what-a-env-file-looks-like">2. what a .env file looks like</h2><p>As I previously said, &#xA0;a .env file is a text file, containing lines of declaration where each declaration represent a single variable. It is conventionally advised to set each variable name inside the file with uppercase words separated by an underscore ( _ ), follow with &#xA0;<code>=</code> &#xA0; (without space) by the value. Here is an example: </p><pre><code class="language-javascript ">VARIABLE_NAME=value</code></pre><h2 id="3-access-it-in-a-nodejs-app">3. Access it in a Node.js app </h2><p>In Node.Js, there is an object called <code>process</code> which is a <code>global</code> that provides information about, and control over, the current Node.js process. As a global, it is always available to Node.js applications without using <code>require()</code> or <code>import</code>. So in Node.js, you will need to write <code>process.env.VARIABLE_NAME</code> to access the value set in the .env file. </p><p>So is it possible to access configurations variables with the <code>process</code> object, what is the purpose of <code>ENVALID</code> ? &#x1F914;</p><p>According to <strong>Envalid</strong> README file, <code>Envalid</code> is a small library for validating and accessing environment variables in Node.js (v8.12 or later) programs, aiming to:</p><ul><li>ensure that your program only runs when all of its environment dependencies are met</li><li>give you executable documentation about the environment your program expects to run in</li><li>give you an immutable API for your environment variables, so they don&apos;t change from under you while the program is running. </li></ul><h2 id="4-setup-%F0%9F%8F%84%F0%9F%8F%BB%E2%80%8D%E2%99%82%EF%B8%8F"> 4. Setup &#x1F3C4;&#x1F3FB;&#x200D;&#x2642;&#xFE0F; </h2><p>In this post, i will use typescript with <a href="https://github.com/jsynowiec/node-typescript-boilerplate">this</a> open-source node-typescript-starter project to show you how it works.</p><ul><li>Open your favorite terminal and clone the starter template with <code>git clone </code><a href="https://github.com/jsynowiec/node-typescript-boilerplate.git"><code>https://github.com/jsynowiec/node-typescript-boilerplate.g</code></a><code>it your_project_name</code> &#xA0;and <code>cd your_project_name</code></li><li>Install node dependencies with <code>npm install</code>. If you are using <code>YARN</code> as a package manager, you will need to delete the <code>package-lock.json</code> at the root folder to avoid conflict with yarn&apos;s <code>-lock.json</code> file. </li><li>Delete the following block inside the package.json (starting at line 5) file to remove targeting a specific node.js version. In this post, we will just focus on looking at how Envalid works. </li></ul><pre><code> &quot;engines&quot;: {
    &quot;node&quot;: &quot;&gt;= 16.13 &lt;17&quot;
  },</code></pre><ul><li>We will also need to install, a useful package that will restart the server each time a file changes and <code>ts-node</code> &#xA0;a typescript execution engine for node. &#xA0;So <code>npm install --save-dev nodemon ts-node</code> &#xA0;</li><li>replace <code>start</code> script inside package.json with the following to add nodemon.</li></ul><pre><code> &quot;start&quot;: &quot;npm run build &amp;&amp; nodemon build/src/main.js&quot;,</code></pre><ul><li>then in the terminal, <code>npm start</code> You should be able to see the output. </li></ul><figure class="kg-card kg-image-card"><img src="https://blog.foujeupavel.com/content/images/2022/01/output-start.png" class="kg-image" alt="Manage your Node.js .env file with Envalid : A clean way to access your secrets keys in prod &#x26A1;&#xFE0F;" loading="lazy" width="1904" height="924" srcset="https://blog.foujeupavel.com/content/images/size/w600/2022/01/output-start.png 600w, https://blog.foujeupavel.com/content/images/size/w1000/2022/01/output-start.png 1000w, https://blog.foujeupavel.com/content/images/size/w1600/2022/01/output-start.png 1600w, https://blog.foujeupavel.com/content/images/2022/01/output-start.png 1904w" sizes="(min-width: 720px) 720px"></figure><p>Now call the default <code>greeter(name:string)</code> a function that returns a Promise with </p><pre><code>greeter(&apos;James Bond&apos;).then((result) =&gt; console.log(result));</code></pre><p>this will automatically trigger a build and print <code>Hello, James Bond</code> in the terminal </p><pre><code class="language-terminal">[nodemon] clean exit - waiting for changes before restart
[nodemon] restarting due to changes...
[nodemon] starting `ts-node src/main.ts`
[nodemon] restarting due to changes...
[nodemon] starting `ts-node src/main.ts`
Hello, James Bond
[nodemon] clean exit - waiting for changes before restart</code></pre><h2 id="5-adding-env-%F0%9F%A4%BE%F0%9F%8F%BC%E2%80%8D%E2%99%82%EF%B8%8F">5. Adding .env &#x1F93E;&#x1F3FC;&#x200D;&#x2642;&#xFE0F;</h2><p>For this app, we will implement something very simple. The app will be a simple one with the main function that displays in the console the redirection link corresponding to a specific user of an array according to its <code>ROLE</code> if we</p><p>create a .env file at the root of the folder and add it to <code>.gitignore</code> file. For this post, let&apos;s consider our app will store 3 variables, that are: &#xA0;</p><ul><li><code>ADMIN_REDIRECT_URL=/v1/custom/path/admin/login</code> &#xA0;which is where to redirect the user if it&apos;s an admin. </li><li><code>DEFAULT_REDIRECT_URL=/v1/custom/path/user/login</code> &#xA0;which is the default redirect url for users</li><li><code>DASHBORD_BASE_URL=https://your_appname.com</code> </li></ul><p>Your .env file should look like this</p><pre><code class="language-.env">ADMIN_REDIRECT_URL=/v1/custom/path/admin/login
DEFAULT_REDIRECT_URL=/v1/custom/path/user/login
DASHBORD_BASE_URL=https://your_appname.com # REPLACE THIS WITH ANY URL 
</code></pre><p>Now let&apos;s access these variables in our app. </p><p>install <strong>Envalid</strong> with <code>npm install envalid</code> or <code>yarn add envalid</code> if you are using yarn. </p><p>create a new file <code>env.ts</code> inside the <code>src</code> folder and add the following code. </p><pre><code>import { cleanEnv, str } from &apos;envalid&apos;;
const env = cleanEnv(process.env, {
  ADMIN_REDIRECT_URL: str({ default: &apos;any_default_url&apos; }),
  DEFAULT_REDIRECT_URL: str({ default: &apos;any_default_url&apos; }),
  DASHBORD_BASE_URL: str({ default: &apos;any_default_url&apos; }),
});
export default env;
</code></pre><p>replace the code inside <code>main.ts</code> file with the following :</p><pre><code class="language-javascript">import env from &apos;./env&apos;;

enum UserType {
  admin,
  customer,
}
interface User {
  name: string;
  role: UserType;
}
const users = [
  { name: &apos;John do&apos;, role: UserType.admin },
  { name: &apos;Marc Vince&apos;, role: UserType.customer },
  { name: &apos;Stephen Huke&apos;, role: UserType.admin },
];

const printHello = (user: User): string =&gt; {
  const { name, role } = user;
  if (role === UserType.admin) {
    return `Hello ${name}! , Your login URL is: ${env.DASHBORD_BASE_URL}${env.ADMIN_REDIRECT_URL}`;
  }
  return `Hello ${name}! , Your login URL is: ${env.DASHBORD_BASE_URL}${env.DEFAULT_REDIRECT_URL}`;
};

const start = () =&gt; {
  users.forEach((user) =&gt; {
    console.log(printHello(user));
  });
};

start();
</code></pre><p>This will print the following in the console :</p><pre><code>[nodemon] starting `ts-node src/main.ts`
Hello John do! , Your login URL is: https://your_appname.com/v1/custom/path/admin/login
Hello Marc Vince! , Your login URL is: https://your_appname.com/v1/custom/path/user/login
Hello Stephen Huke! , Your login URL is: https://your_appname.com/v1/custom/path/admin/login</code></pre><p>You can see that the .env variables have been well displayed. </p><h2 id="6-envalid-api">6. Envalid API </h2><p>if you look at the <code>env.ts</code> file, you will notice the import of <code>cleanEnv()</code> to load environment variables. <code>cleanEnv()</code> returns a sanitized, immutable environment object, and accepts three positional arguments:</p><ul><li><code>environment</code> - An object containing your env vars (eg. <code>process.env</code>)</li><li><code>validators</code> - An object that specifies the format of required vars.</li><li><code>options</code> - An (optional) object, which supports the following key:</li><li><code>reporter</code> - Pass in a function to override the default error handling and console output. </li></ul><p>By using <code>cleanEnv()</code>, it will log an error message and exit (in Node) or throw (in browser) if any required env vars are missing or invalid. You can override this behavior by writing your own reporter. </p><h2 id="7-validator-types">7. Validator types</h2><p>Node&apos;s <code>process.env</code> only stores strings, but sometimes you want to retrieve other types (booleans, numbers), or validate that an env var is in a specific format (JSON, URL, email address). To this end, the following validation functions are available:</p><ul><li><code>str()</code> - Passes string values through, will ensure a value is present unless a <code>default</code> value is given. Note that an empty string is considered a valid value - if this is undesirable you can easily create your own validator (see below)</li><li><code>bool()</code> - Parses env var strings <code>&quot;1&quot;, &quot;0&quot;, &quot;true&quot;, &quot;false&quot;, &quot;t&quot;, &quot;f&quot;</code> into booleans</li><li><code>num()</code> - Parses an env var (eg. <code>&quot;42&quot;, &quot;0.23&quot;, &quot;1e5&quot;</code>) into a Number</li><li><code>email()</code> - Ensures an env var is an email address</li><li><code>host()</code> - Ensures an env var is either a domain name or an ip address (v4 or v6)</li><li><code>port()</code> - Ensures an env var is a TCP port (1-65535)</li><li><code>url()</code> - Ensures an env var is a URL with a protocol and hostname</li><li><code>json()</code> - Parses an env var with <code>JSON.parse</code></li></ul><p>Each validation function accepts an (optional) object with the following attributes:</p><ul><li><code>choices</code> - An Array that lists the admissible parsed values for the env var.</li><li><code>default</code> - A fallback value, which will be present in the output if the env var wasn&apos;t specified. Providing a default effectively makes the env var optional. Note that <code>default</code> values are not passed through validation logic.</li><li><code>devDefault</code> - A fallback value to use <em>only</em> when <code>NODE_ENV</code> is <em>not</em> <code>&apos;production&apos;</code>. This is handy for env vars that are required for production environments but optional for development and testing.</li><li><code>desc</code> - A string that describes the env var.</li><li><code>example</code> - An example value for the env var.</li><li><code>docs</code> - A url that leads to more detailed documentation about the env var.</li></ul><h2 id="8-custom-validators">8. Custom validators</h2><p>You can easily create your own validator functions with <code>envalid.makeValidator()</code>. It takes a function as its only parameter, and should either return a cleaned value or throw if the input is unacceptable:</p><pre><code class="language-javascript">import { makeValidator, cleanEnv } from &apos;envalid&apos;
const twochars = makeValidator(x =&gt; {
    if (/^[A-Za-z]{2}$/.test(x)) return x.toUpperCase()
    else throw new Error(&apos;Expected two letters&apos;)
})

const env = cleanEnv(process.env, {
    INITIALS: twochars()
});
</code></pre><h2 id="9-error-reporting">9. Error Reporting</h2><p>By default, if any required environment variables are missing or have invalid values, <strong>Envalid</strong> will log a message and call <code>process.exit(1)</code>. You can override this behavior by passing in your own function as <code>options.reporter</code>. For example:</p><pre><code class="language-javascript">const env = cleanEnv(process.env, myValidators, {
    reporter: ({ errors, env }) =&gt; {
        emailSiteAdmins(&apos;Invalid env vars: &apos; + Object.keys(errors))
    }
})</code></pre><p>Additionally, <strong>Envalid</strong> exposes <code>EnvError</code> and <code>EnvMissingError</code>, which can be checked in case specific error handling is desired:</p><pre><code class="language-javascript">const env = cleanEnv(process.env, myValidators, {
    reporter: ({ errors, env }) =&gt; {
        for (const [envVar, err] of Object.entries(errors)) {
            if (err instanceof envalid.EnvError) {
                ...
            } else if (err instanceof envalid.EnvMissingError) {
                ...
            } else {
                ...
            }
        }
    }
})
</code></pre><p>Thanks for reading, the source code is available <a href="https://github.com/Doha26/fullstack-hebdo-posts/tree/main/01-discover-envalid-nodejs">here</a>. </p><p><em>Follow me on social media to stay up to date with the latest posts.</em> &#xA0; </p>]]></content:encoded></item><item><title><![CDATA[Frontend , Backend , Full-stack ,  etc. Choose the  one  which suits you best 😉]]></title><description><![CDATA[<p>When you start working as a software developer, there are plenty area you can get into : Frontend, Backend, Full-stack, DevOps, Mobile Apps, Game, Security, Desktop, Embedded, etc). Most of the time, they can be grouped in 4 majors categories. <strong>Frontend</strong>, <strong>Backend</strong>, <strong>FullStack</strong>, <strong>DevOps </strong>Engineers. </p><p>If you make a quick search</p>]]></description><link>https://blog.foujeupavel.com/frontend-backend-full-stack-etc-choose-the-one-which-suits-you-best/</link><guid isPermaLink="false">61c53230a72f86064975aedf</guid><category><![CDATA[Usefull]]></category><dc:creator><![CDATA[Pavel FOUJEU]]></dc:creator><pubDate>Sat, 25 Dec 2021 08:40:39 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1498050108023-c5249f4df085?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGNvZGluZ3xlbnwwfHx8fDE2NDA0MTY1NTQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1498050108023-c5249f4df085?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGNvZGluZ3xlbnwwfHx8fDE2NDA0MTY1NTQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Frontend , Backend , Full-stack ,  etc. Choose the  one  which suits you best &#x1F609;"><p>When you start working as a software developer, there are plenty area you can get into : Frontend, Backend, Full-stack, DevOps, Mobile Apps, Game, Security, Desktop, Embedded, etc). Most of the time, they can be grouped in 4 majors categories. <strong>Frontend</strong>, <strong>Backend</strong>, <strong>FullStack</strong>, <strong>DevOps </strong>Engineers. </p><p>If you make a quick search with any of theses key words, you will have lots of articles describing each one and associated roles. In this article, i&apos;m not going to go deeper into each of them, i will just give you necessary keys to choose the one that suits you best according to your preferences. </p><p>First of all, being a software developer is all about building user experiences that suit and convert end users. As a developer, building all of this requires to master (at least a certain level) of &#xA0;tools and technologies needed to do it. Thus, it sometimes requires within the same team different&apos;s profile of developers. Here i will describe and you will have to choose where you could be the most comfortable. </p><ol><li><strong>Frontend &#xA0;(Client-Side)</strong></li></ol><p>A Front-end developer is the one specialized in building user interfaces (UI). He is responsible to deliver a great visual experience to the end user who access the app trough a browser or any client side device like mobile app, Desktop app, etc. &#xA0;Since the result of his work is directly presented to the end user, he must have mastered certain key concepts such as animations, man-machine interactions, responsive design, design systems etc.<br>Most of the time, everything visual is done by Front-end. The skills required to be Front-end basically include : </p><ul><li>For Web development: HTML, CSS (Sass, Less), JavaScript. With the current evolutions of the web and the development of Internet, it will be great to add to the tech stack UI frameworks such as <strong>Tailwind CSS</strong>, <strong>Bootstrap</strong>, <strong>Bulma</strong>, <strong>Semantic UI</strong>, <strong>Foundation</strong>, <strong>Materialize</strong>, <strong>Chakra UI</strong> etc. (I cannot name them all &#x1F609;), javascript frameworks like <strong>React</strong>, <strong>Vue.js</strong>, <strong>Ember.js</strong>, <strong>Meteor.js</strong>, <strong>jQuery</strong>, etc.</li><li>For Mobile Apps : Mobile apps can be built using two approach. Native platform specific SDK (Android &#xA0;sdk for android apps, IOS sdk for ios apps) or cross-platform framework such as React Native, Cordova, Ionic, NativeScript, Appcelerator, Flutter, Xamarin, RubyMotion, etc. Most of theses framework uses javascript</li><li>For Desktop apps : A desktop developer is a programmer who writes code for software applications that (1) run natively on operating systems like macOS, Windows, and Linux. Desktop apps can be built with programming language such as C/C++, JavaFX, Python, C#, Ruby, Swift, Objective-C. </li></ul><p> Since we are more productive with the tools we are most comfortable with, if you think you are more proficient and more comfortable producing rich user interfaces through animation and visuals, the front is for you. For more details, take a look here to see an example of &#xA0;front-end developer &#xA0;roadmap <a href="https://roadmap.sh/frontend">https://roadmap.sh/frontend</a>. </p><p><strong>2. &#xA0;Backend (Server-Side)</strong></p><p>The backend developer specializes in design, implementation, functional logic and performance and scalability of a piece of software or system running on machines that are remote from the end-user. Backend development refers to server-side development that centers on scripting, databases, and web application architecture. In simple terms, the backend developer handles behind-the-scenes activities that take place while performing a function on the website or application. It&#x2019;s the backend developer&#x2019;s responsibility to write code that communicates with databases or APIs, creating libraries, working on data architecture and business processes, and so on. Back-end systems can grow to be very complex, but their complexity is often not visible to the users. The whole complexity is absorbed on the back to deliver the best experience to the user on the front. Backend developer should have strong algorithm skills and will deal with programming languages such as Java, C, C++, ruby, Perl, Python, Scala, Go, Javascript, etc. He should be also been able to understand concepts such as microservices, APIs, distributed systems, storage, databases, Continuous delivering, Architectural Patterns, Scalability ( scaling Vertical vs Horizontal) caching, logging, metrics measurement, etc. For more details, take a look here to see an example of &#xA0;back-end developer &#xA0;roadmap <a href="https://roadmap.sh/backend">https://roadmap.sh/backend</a>. </p><p><strong>3. &#xA0;Full-stack</strong></p><p> A Full Stack Developer is someone who works with the Back End &#x2014; or server side &#x2014; of the application as well as the Front End, or client side. Full Stack Developers have to have some <strong>skills in a wide variety of coding niches</strong>, from databases to graphic design and UI/UX management in order to do their job well. They are something of a swing, ready to assist wherever needed in the process. </p><p>Some of the responsibilities of a Full Stack Developer include:</p><ul><li>Helping with the design and development of software</li><li>Testing and debugging software to keep it optimized</li><li>Writing clean code for the front and back end of the software</li><li>Designing user interactions on the web application itself</li><li>Creating servers and databases for the back end of the software</li><li>Ensuring cross-platform compatibility and optimization</li><li>Testing and maintaining the responsive design of applications</li><li>Working with graphic designers to design new features</li><li>Developing APIs and RESTful services</li><li>Keeping up with technological advances to optimize their software</li><li>Communicating effectiveness of emerging technologies to decision makers</li><li>Considering security, maintenance, scalability, and more when developing</li></ul><p><strong>4. &#xA0;DevOps</strong></p><p>A DevOps engineer is an IT generalist who should have a wide-ranging knowledge of both development and operations, including coding, infrastructure management, system administration, and DevOps toolchains. DevOps engineers should also possess interpersonal skills since they work across company silos to create a more collaborative environment. Some core concepts that constitute the DevOps process can be:</p><ul><li> Agile planning</li><li>Continuous development</li><li>Continuous automated testing</li><li>Continuous integration and continuous delivery (CI/CD)</li><li>Continuous deployment</li><li>Continuous monitoring</li><li>Infrastructure as a code</li><li>Containerization</li><li>Microservices</li><li>Cloud infrastructure</li><li>System architecture and provisioning</li></ul><p>A DevOps engineer must have programming experience to cover scripting and coding. Scripting skills usually entail the knowledge of Bash or PowerShell scripts, while coding skills may include<a href="https://www.altexsoft.com/blog/engineering/pros-and-cons-of-java-programming/"> </a>Java, C#, C++, Python, PHP, Ruby, etc., or at least some of these languages, Knowledge of database systems, strong communication and collaboration skills, experience with automation tools. Take a look here too see more <a href="https://roadmap.sh/devops">https://roadmap.sh/devops</a></p><p>Thanks for reading. </p><p><em>Follow me on socials media to stay up to date with latest posts.</em> </p>]]></content:encoded></item></channel></rss>