<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged graphql</title>
<link>https://coderlegion.com/tag/graphql</link>
<description>Powered by Question2Answer</description>
<item>
<title>How to Decide Whether a WAF Can Protect a GraphQL API</title>
<link>https://coderlegion.com/32040/how-to-decide-whether-a-waf-can-protect-a-graphql-api</link>
<description>&lt;p&gt;Protecting a GraphQL API with a WAF starts with an honest inventory of the HTTP conversation, not with a claim that the firewall understands your schema. SafeLine is a self-hosted web application firewall that filters and monitors HTTP traffic between a web application and the Internet, and it works as a reverse proxy so clients reach the shield before they reach the origin (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). The same product docs describe it as an HTTP and HTTPS reverse proxy built on nginx: it receives traffic for the original site, cleans malicious traffic, and forwards the remaining traffic upstream (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&lt;/a&gt;). Those pages do not describe a GraphQL parser, query-depth limit, complexity budget, or persisted-query allowlist. This checklist is the decision guide I use when the question is how to protect a GraphQL API with a WAF and the product evidence only covers general HTTP controls.&lt;/p&gt;
&lt;p&gt;Start with the listener, not the resolvers. In the SafeLine console, an application is defined by a domain, hostname, or IP, a listen port such as 80 or 443, an SSL option for HTTPS apps, and an upstream address that receives forwarded traffic (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&lt;/a&gt;). After that, the domain must resolve to the SafeLine server. For GraphQL, write down the exact path, methods, and content types your clients use. A browser app that posts JSON to a single endpoint is a different operational problem from a partner integration that posts the same shape from a server. Both are still HTTP. Neither fact, by itself, means SafeLine will interpret operation names.&lt;/p&gt;
&lt;p&gt;Separate controls that belong in the API from controls the WAF documents. Field authorization, introspection policy, alias limits, depth limits, and cost analysis have to live in the GraphQL server or gateway. Do not treat a missing integration note as a hidden feature. What the WAF docs do list is broader web-attack defense, proactive bot-abuse defense, HTML and JS code encryption, IP-based rate limiting, and a web access control list (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). The attack list includes SQL injection, XSS, code injection, OS command injection, CRLF injection, LDAP injection, XPath injection, RCE, XXE, SSRF, path traversal, backdoor, brute force, HTTP flood, and bot abuse. Those checks can matter when a GraphQL variable carries a classic payload, because the bytes still travel in an HTTP request. They are not a substitute for resolver authorization, and they do not prove that every malicious query will be recognized as GraphQL abuse.&lt;/p&gt;
&lt;p&gt;Use rate limiting where the abuse is volume, not query shape. SafeLine rate limiting is documented as a way to throttle traffic that exceeds defined limits, including DoS, brute-force attempts, traffic surges, and other abuse (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). That is a reasonable layer in front of a login mutation or a hot query path that is being flooded. It does not count field complexity. If one small request can expand into thousands of resolver calls, fix that in the API even if the firewall never raises an alarm.&lt;/p&gt;
&lt;p&gt;Be careful with challenges. CAPTCHA is documented to allow human users and block crawlers and bots. An authentication challenge, when turned on, requires visitors to enter a password or be blocked (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). Those controls can fit a browser-only GraphQL console. They can break mobile apps, server jobs, and other non-browser clients. Put that client inventory on the checklist before you enable either challenge on the API host. Dynamic protection, which encrypts HTML and JS on each visit, is also documented (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). A pure JSON GraphQL response is not an HTML or JS page, so do not plan on that feature as API protection.&lt;/p&gt;
&lt;p&gt;Validate with the tests the docs actually publish, then read the console. The application guide shows sample requests for SQL injection, XSS, path traversal, code injection, and XXE, and says a blocking page means the attack was defended. Attack detail is available in the web console (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&lt;/a&gt;). You can repeat the same payload classes inside a GraphQL variable as a manual check that the HTTP body is visible to inspection. A block is evidence about that payload, not evidence of schema-aware coverage. A pass is not proof that the query is safe.&lt;/p&gt;
&lt;p&gt;Check the host before cutover. The install guide requires Linux, x86_64 or arm64, Docker 20.10.14 or above, Docker Compose 2.0.0 or above, and at least 1 CPU core, 1 GB of memory, and 5 GB of disk, plus a CPU with ssse3 (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/Deploy&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/Deploy&lt;/a&gt;). The same page says SafeLine on ARM requires a Pro license and that the Personal Edition is not supported on ARM. The product home page states that SafeLine is production-ready, with over 180,000 installations, protection for over 1,000,000 websites, and more than 30,000,000,000 HTTP requests handled daily (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). As of September 30, 2026, SafeLine surpassed 700,000 installations worldwide. SafeLine Pro annual promotional pricing is $650-$880 versus the $1,000 regular price.&lt;/p&gt;
&lt;p&gt;If the checklist fails at any point where you needed a GraphQL-specific control, keep that control in the API. Use the WAF for the HTTP jobs its docs describe: reverse-proxy placement, attack filtering, rate limiting, access control, and optional human challenges on browser traffic.&lt;/p&gt;
&lt;h4 id=&quot;does-safeline-document-a-graphql-rule-pack-&quot;&gt;Does SafeLine document a GraphQL rule pack?&lt;/h4&gt;
&lt;p&gt;No. The supplied product docs describe general HTTP and HTTPS filtering, reverse-proxy application setup, and the controls listed above. They do not document GraphQL query parsing, depth limits, or complexity scoring. Plan those in the API.&lt;/p&gt;
&lt;h4 id=&quot;should-every-graphql-client-pass-a-captcha-&quot;&gt;Should every GraphQL client pass a CAPTCHA?&lt;/h4&gt;
&lt;p&gt;No. CAPTCHA and the authentication challenge are documented visitor checks. They fit some browser flows and can block legitimate non-browser clients. Decide from your client list, not from a generic API template.&lt;/p&gt;
&lt;h4 id=&quot;is-ip-rate-limiting-enough-for-an-expensive-query-&quot;&gt;Is IP rate limiting enough for an expensive query?&lt;/h4&gt;
&lt;p&gt;No. Documented rate limiting throttles traffic that exceeds defined limits. It does not measure resolver cost. Keep complexity limits in the GraphQL server, and use rate limiting for floods and brute force.&lt;/p&gt;
&lt;h4 id=&quot;what-do-i-check-after-the-domain-points-at-safeline-&quot;&gt;What do I check after the domain points at SafeLine?&lt;/h4&gt;
&lt;p&gt;Confirm domain, port, SSL choice, and upstream in the application dialog, then confirm clients still reach the origin only through the proxy (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&lt;/a&gt;). Review the attack list when you replay the documented test payloads. Treat GraphQL-only abuse cases as API tests, not as undocumented firewall features.&lt;/p&gt;
&lt;p&gt;If you want a second pair of eyes on this checklist before cutover, review the project and the official docs, then compare the current Pro promotion with your deployment constraints.&lt;/p&gt;
&lt;p&gt;Project: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;https://github.com/chaitin/SafeLine&lt;/a&gt;&lt;br&gt;
Docs: &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;&lt;br&gt;
Promotion: &lt;a rel=&quot;nofollow&quot; href=&quot;https://cyberserval.tech/landing/lto&quot;&gt;https://cyberserval.tech/landing/lto&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32040/how-to-decide-whether-a-waf-can-protect-a-graphql-api</guid>
<pubDate>Thu, 22 Oct 2026 08:00:23 +0000</pubDate>
</item>
<item>
<title>How to Decide If a WAF Belongs in Front of GraphQL</title>
<link>https://coderlegion.com/32039/how-to-decide-if-a-waf-belongs-in-front-of-graphql</link>
<description>&lt;p&gt;A GraphQL API is still an HTTP application with a narrow front door. Most deployments expose one path, accept a document plus variables, and return JSON. That shape invites two mistakes: leaving the origin directly reachable, or assuming a web application firewall understands selections, fragments, and resolver cost. This checklist uses only controls described in the SafeLine documentation. Where those pages are silent, the item stays on the API, not in the proxy.&lt;/p&gt;
&lt;p&gt;SafeLine is a self-hosted WAF. It filters and monitors HTTP traffic and is designed to block common web attacks, including SQL injection, XSS, code injection, OS command injection, CRLF injection, LDAP injection, XPath injection, RCE, XXE, SSRF, path traversal, backdoors, brute force, HTTP flood, and bot abuse (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). It works as a reverse proxy: clients pass through it before they reach the server, and it forwards traffic it does not treat as malicious (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&lt;/a&gt;). Nothing on those pages describes a GraphQL parser, an introspection switch, a complexity score, or a persisted-query catalog. If you need those, build them in the GraphQL server. A WAF placement is still worth a decision, because the same endpoint is ordinary HTTP and can carry injection payloads in headers, query strings, and bodies.&lt;/p&gt;
&lt;h3 id=&quot;placement-checklist&quot;&gt;Placement checklist&lt;/h3&gt;
&lt;p&gt;Work through these gates before you change a production record.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Confirm the protocol. SafeLine protects web apps by proxying HTTP and HTTPS. If the API is not reached that way, stop. Do not invent a side integration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pick the hostname clients already trust. In Applications, Add Application asks for Domain, Port, and Upstream. Use 443 and enable SSL for HTTPS, or 80 for plain HTTP. Upstream is the real address of the GraphQL process. After saving, resolve that domain to the SafeLine server so clients never need the origin address (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Map each fear to a documented control. Injection and related web attacks map to the attack defenses above. Floods, brute force, and traffic surges map to rate limiting, which the docs describe as throttling traffic that exceeds defined limits, and the core list calls out IP-based rate limiting (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). A web access control list is also named there. Do not write rule syntax the guides do not provide; configure it in the console and verify the result.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Treat human challenges as optional, not as API authentication. Captcha is documented to allow human users and block crawlers and bots. Authentication challenge blocks visitors who do not enter the password. Either can shield a browser IDE pointed at &lt;code&gt;/graphql&lt;/code&gt;. Either can also black-hole a mobile client or a partner job. If no human is in the loop, leave both off.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Skip dynamic HTML and JS encryption for a JSON API. That feature encrypts HTML and JS on each visit. It is not a control for GraphQL documents or JSON responses.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Match the host to the install guide before the pilot. You need Linux on x86_64 or arm64, Docker 20.10.14 or newer, Docker Compose 2.0.0 or newer, at least one CPU core, 1 GB of memory, 5 GB of disk, and a CPU with SSSE3 (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/Deploy&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/Deploy&lt;/a&gt;). On ARM, a Pro license is required; the Personal Edition is not supported on ARM. The same page notes that the LTS line is no longer maintained starting from 9.1.0-LTS on 14 August 2025, and recommends the current release for new setups.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prove the path with the published probes, then with your own POST. The application guide lists SQL injection, XSS, path traversal, code injection, and XXE samples as query strings, and tells you to expect a block page and to open the attack detail in the console. Run those against a test hostname. Then send a normal GraphQL query, a mutation, and a deliberately hostile string inside a variable. A query-string block does not, by itself, prove how a JSON body will be classified. The console is the evidence, not a guess.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep schema policy at the origin. Depth limits, alias and batch ceilings, field authorization, and introspection policy are application concerns. SafeLine is not documented as a GraphQL gateway. Putting it in front does not authorize you to delete those checks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rate limits need a GraphQL-specific reading even though the feature is generic. One route receives almost every operation, so a low ceiling can punish a busy office NAT or a single backend egress address. Start from real client-IP rates, then tighten. Review the attack list after the change so a false block is visible before customers report it.&lt;/p&gt;
&lt;p&gt;The home documentation calls the product production-ready, protecting over 1,000,000 websites and handling over 30,000,000,000 HTTP requests daily, and it publishes a general detection evaluation on 33,669 samples (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;). Those numbers are not a GraphQL score. Do not reuse them as one. As of September 30, 2026, SafeLine surpassed 700,000 installations worldwide. SafeLine Pro annual promotional pricing is $650-$880 versus the $1,000 regular price.&lt;/p&gt;
&lt;h4 id=&quot;does-the-documentation-describe-graphql-body-parsing-&quot;&gt;Does the documentation describe GraphQL body parsing?&lt;/h4&gt;
&lt;p&gt;No. It describes HTTP and HTTPS reverse-proxy filtering and a list of web-attack defenses. Plan for that, and test a real POST, instead of assuming a schema-aware inspection engine.&lt;/p&gt;
&lt;h4 id=&quot;where-do-clients-send-graphql-after-cutover-&quot;&gt;Where do clients send GraphQL after cutover?&lt;/h4&gt;
&lt;p&gt;They send it to the domain you added in Applications. That domain must resolve to the SafeLine host. SafeLine forwards cleaned traffic to the upstream you configured.&lt;/p&gt;
&lt;h4 id=&quot;can-captcha-replace-api-keys-&quot;&gt;Can captcha replace API keys?&lt;/h4&gt;
&lt;p&gt;No. Captcha and the authentication challenge are visitor gates for humans and bots, as documented. Credential checks for apps, jobs, and partners still belong to your API.&lt;/p&gt;
&lt;h4 id=&quot;what-fails-an-install-before-graphql-is-even-involved-&quot;&gt;What fails an install before GraphQL is even involved?&lt;/h4&gt;
&lt;p&gt;A host that misses the Docker, Compose, CPU, memory, disk, or SSSE3 requirements will not match the install guide. An ARM host without a Pro license is also the wrong pilot target, because the Personal Edition is not supported on ARM.&lt;/p&gt;
&lt;h4 id=&quot;what-should-the-first-success-metric-be-&quot;&gt;What should the first success metric be?&lt;/h4&gt;
&lt;p&gt;A documented attack sample blocked on a test hostname, a visible detail record in the console, and a legitimate GraphQL operation from a real client that still succeeds. Adoption of the proxy is not proof that resolver cost is under control.&lt;/p&gt;
&lt;p&gt;If you are about to publish a GraphQL endpoint, walk this checklist, install from the official guide, and add the application before you argue about schema limits. As of September 30, 2026, SafeLine surpassed 700,000 installations worldwide. SafeLine Pro annual promotional pricing is $650-$880 versus the $1,000 regular price. You can review the project, the docs, and the current promotion here:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;https://github.com/chaitin/SafeLine&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://cyberserval.tech/landing/lto&quot;&gt;https://cyberserval.tech/landing/lto&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32039/how-to-decide-if-a-waf-belongs-in-front-of-graphql</guid>
<pubDate>Thu, 22 Oct 2026 07:59:52 +0000</pubDate>
</item>
<item>
<title>Getting Started with GraphQL in ASP.NET</title>
<link>https://coderlegion.com/24083/getting-started-with-graphql-in-asp-net</link>
<description>&lt;p&gt;&lt;strong&gt;Getting Started with GraphQL in ASP.NET and EF Core&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GraphQL is a modern API technology that allows clients to request exactly the data they need. Unlike traditional REST APIs, GraphQL uses a single endpoint and gives consumers more control over the response structure.&lt;/p&gt;
&lt;p&gt;If you're building ASP.NET applications with Entity Framework Core, adding GraphQL can be a great way to expose your data efficiently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why GraphQL?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With REST APIs, clients often receive more data than they need or must call multiple endpoints to collect related information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GraphQL helps solve these problems by providing:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flexible data retrieval&lt;br&gt;
Fewer API requests&lt;br&gt;
Strongly typed schemas&lt;br&gt;
Better performance for complex data scenarios&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Create a New ASP.NET Project&lt;/p&gt;
&lt;p&gt;Start by creating a Web API project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dotnet new webapi -n GraphQLDemo
cd GraphQLDemo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Install the required packages:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dotnet add package HotChocolate.AspNetCore
dotnet add package HotChocolate.Data.EntityFramework
dotnet add package Microsoft.EntityFrameworkCore.InMemory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the Entity&lt;/p&gt;
&lt;p&gt;Let's create a simple Product model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public decimal Price { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Configure EF Core&lt;/p&gt;
&lt;p&gt;Create a DbContext:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions&amp;lt;AppDbContext&amp;gt; options)
        : base(options)
    {
    }

    public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a GraphQL Query&lt;/p&gt;
&lt;p&gt;GraphQL queries define the data clients can request.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Query
{
    public IQueryable&amp;lt;Product&amp;gt; GetProducts(
        [Service] AppDbContext context)
    {
        return context.Products;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Returning IQueryable allows Hot Chocolate to optimize filtering, sorting, and projections.&lt;/p&gt;
&lt;p&gt;Register Services&lt;/p&gt;
&lt;p&gt;In Program.cs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext&amp;lt;AppDbContext&amp;gt;(options =&amp;gt;
    options.UseInMemoryDatabase(&quot;ProductsDb&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;builder.Services
    .AddGraphQLServer()
    .AddQueryType&amp;lt;Query&amp;gt;();

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService&amp;lt;AppDbContext&amp;gt;();

    db.Products.AddRange(
        new Product { Id = 1, Name = &quot;Laptop&quot;, Price = 999.99m },
        new Product { Id = 2, Name = &quot;Mouse&quot;, Price = 29.99m }
    );

    db.SaveChanges();
}

app.MapGraphQL();

app.Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run Your First Query&lt;/p&gt;
&lt;p&gt;Navigate to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://localhost:5001/graphql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;query {
  products {
    id
    name
    price
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Response:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;data&quot;: {
    &quot;products&quot;: [
      {
        &quot;id&quot;: 1,
        &quot;name&quot;: &quot;Laptop&quot;,
        &quot;price&quot;: 999.99
      },
      {
        &quot;id&quot;: 2,
        &quot;name&quot;: &quot;Mouse&quot;,
        &quot;price&quot;: 29.99
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enable Filtering and Sorting&lt;/p&gt;
&lt;p&gt;One of the best features of Hot Chocolate is built-in filtering and sorting.&lt;/p&gt;
&lt;p&gt;Update the query:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using HotChocolate.Data;

public class Query
{
    [UseFiltering]
    [UseSorting]
    public IQueryable&amp;lt;Product&amp;gt; GetProducts(
        [Service] AppDbContext context)
    {
        return context.Products;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Register the features:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;builder.Services
    .AddGraphQLServer()
    .AddQueryType&amp;lt;Query&amp;gt;()
    .AddFiltering()
    .AddSorting();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now clients can run queries like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;query {
  products(
    where: { price: { gt: 100 } }
    order: { price: DESC }
  ) {
    name
    price
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conclusion&lt;/p&gt;
&lt;p&gt;GraphQL and EF Core work very well together in ASP.NET applications. With Hot Chocolate, you can expose database entities through GraphQL with minimal configuration while gaining powerful features like filtering, sorting, and projections.&lt;/p&gt;
&lt;p&gt;If you're already using EF Core, adding GraphQL is an easy way to make your APIs more flexible and developer-friendly. Start with simple queries, then move on to mutations, authentication, and real database providers like SQL Server or PostgreSQL.&lt;/p&gt;
&lt;p&gt;Happy coding! 🚀&lt;/p&gt;
&lt;p&gt;References&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GraphQL Official Documentation, Learn GraphQL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://graphql.org/learn/&quot;&gt;https://graphql.org/learn/&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GraphQL Official Documentation, Queries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://graphql.org/learn/queries/&quot;&gt;https://graphql.org/learn/queries/&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hot Chocolate Documentation, Getting Started with GraphQL in .NET&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://chillicream.com/docs/hotchocolate/get-started-with-graphql-in-net-core&quot;&gt;https://chillicream.com/docs/hotchocolate/get-started-with-graphql-in-net-core&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hot Chocolate Documentation, GraphQL Server for .NET&lt;br&gt;
 &lt;a rel=&quot;nofollow&quot; href=&quot;https://chillicream.com/docs/hotchocolate&quot;&gt;https://chillicream.com/docs/hotchocolate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24083/getting-started-with-graphql-in-asp-net</guid>
<pubDate>Tue, 04 Aug 2026 08:44:29 +0000</pubDate>
</item>
<item>
<title>Getting Started with GraphQL for Laravel Developers</title>
<link>https://coderlegion.com/12069/getting-started-with-graphql-for-laravel-developers</link>
<description>&lt;p&gt;Unlock the potential of GraphQL in your Laravel applications. This guide covers the fundamentals, advantages over REST, and practical ways to integrate GraphQL into your projects.&lt;/p&gt;
&lt;h3 id=&quot;introduction-to-graphql&quot;&gt;Introduction to GraphQL&lt;/h3&gt;
&lt;p&gt;GraphQL is a powerful query language for APIs, initially developed by Facebook. Unlike REST, which exposes multiple endpoints, GraphQL provides a single endpoint. Here, you can request only the data you need, reducing payload size and improving speed.&lt;/p&gt;
&lt;h3 id=&quot;architecture-of-graphql&quot;&gt;Architecture of GraphQL&lt;/h3&gt;
&lt;p&gt;At its core, GraphQL is built around the concepts of types and fields. You define a schema that describes what queries can be made and what types of data each query returns. This schema acts as a contract between the client and the server.&lt;/p&gt;
&lt;h4 id=&quot;key-components-of-graphql-&quot;&gt;Key Components of GraphQL:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Queries&lt;/strong&gt;: Read data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mutations&lt;/strong&gt;: Write data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subscriptions&lt;/strong&gt;: Listen to real-time updates.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;advantages-over-rest&quot;&gt;Advantages Over REST&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Single Endpoint&lt;/strong&gt;: Eliminate the need for multiple URLs, streamlining your API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client-Driven&lt;/strong&gt;: Clients request exactly what they need, minimizing over-fetching and under-fetching.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strongly Typed&lt;/strong&gt;: Changes to the data structure can be managed more easily due to the schema definition.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h3&gt;
&lt;h4 id=&quot;integrating-graphql-with-laravel&quot;&gt;Integrating GraphQL with Laravel&lt;/h4&gt;
&lt;p&gt;To start using GraphQL in your Laravel application, follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install the Package&lt;/strong&gt;:&lt;br&gt;
You can use the &lt;code&gt;lighthouse-php&lt;/code&gt; package, which is a popular choice for integrating GraphQL in Laravel:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;composer require nuwave/lighthouse
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Publish the Config File&lt;/strong&gt;:&lt;br&gt;
Run the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;php artisan vendor:publish --tag=lighthouse
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Define Your Schema&lt;/strong&gt;:&lt;br&gt;
Create a &lt;code&gt;graphql/schema.graphql&lt;/code&gt; file. Here’s a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphql&quot;&gt;type Query {
    users: [User]
}
type User {
    id: ID!
    name: String!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Resolvers&lt;/strong&gt;:&lt;br&gt;
For the &lt;code&gt;users&lt;/code&gt; query, you might set up a resolver in &lt;code&gt;app/GraphQL/Queries/UserQuery.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;namespace App\GraphQL\Queries;
use App\Models\User;
   
class UserQuery {
    public function resolve($root, array $args) {
        return User::all();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing Your GraphQL API&lt;/strong&gt;:&lt;br&gt;
You can use tools like Postman or GraphQL Playground to play around with your API. Here’s a simple query to get started:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphql&quot;&gt;query {
    users {
        id
        name
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;GraphQL offers a flexible way to interact with your data, which can improve performance and reduce the time it takes to build features. By adopting these principles, you can create APIs that better serve the needs of your applications. Start experimenting, and you might find that GraphQL changes how you think about API design altogether.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/12069/getting-started-with-graphql-for-laravel-developers</guid>
<pubDate>Mon, 23 Feb 2026 06:56:26 +0000</pubDate>
</item>
<item>
<title>End-to-End GraphQL File Uploads with Apollo Server and Apollo Client: A Comprehensive Guide</title>
<link>https://coderlegion.com/5100/end-to-end-graphql-file-uploads-with-apollo-server-and-apollo-client-comprehensive-guide</link>
<description>&lt;p&gt;We recently started partnering (where I currently work) with some of our customers to co-create content, and as a result we needed a way to stream files from the client to the server, through GraphQL. This added an extra layer of complexity, which took us some time to effectively hash-out, and hence the inspiration for this blog.&lt;/p&gt;
&lt;p&gt;In this blog, I'll be walking you through (with code snippets included) how to enhance your GraphQL set up, to allow for file uploads. I intend this tutorial to be an in-depth one, that goes full circle starting with the server set up, and implementation (using Apollo Server), and going all the way to the client set up and implementation (using Apollo Client). &lt;br&gt;
The tech-stack specifications for the client will be Apollo client (and I'll try and generalize the config as much as possible since there's a gazillion javascript frontend frameworks and I can't possibly touch on all of them. &lt;strong&gt;There'll be probably another new javascript frontend framework by the time you are reading this lol&lt;/strong&gt;).&lt;br&gt;
For the server, the set-up is mostly inclined towards NestJS, but can be co-adopted to other frameworks as well.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;1-spinning-up-the-backend-dependencies-amp-configuration&quot;&gt;1. Spinning Up the Backend: Dependencies &amp;amp; Configuration&lt;/h4&gt;
&lt;p&gt;So for starters, create a nestjs project (if you don't have one already). You can skip this step if you have your own set-up that isn't nestjs-related.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nest new project-name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Secondly, install necessary dependencies (I'm using TypeScript, which should - at the risk of breaking the one forbidden software rule of having an opinion - be your default at this point. If you are writing server-side code without static types, then you must be the one-true master, and I'd love to learn your ways). Skip the nestjs apollo dependencies if you have a different backend set up:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm i @nestjs/graphql @nestjs/apollo @apollo/server@^4.11.2 graphql graphql-upload-ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to configure our graphql set-up. &lt;/p&gt;
&lt;h5 id=&quot;for-the-nestjs-category-&quot;&gt;For the nestjs category:&lt;/h5&gt;
&lt;p&gt;Open &lt;code&gt;app.module.ts&lt;/code&gt; and make sure your graphql config matches mine (especially since v10 of Apollo Server removed it's built-in upload handler):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// ...other imports
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
// ...other imports
GraphQLModule.forRoot&amp;lt;ApolloDriverConfig&amp;gt;({
  driver: ApolloDriver,
  playground: false, // Let's get fancy with an alternative playground.
  plugins: [
    ApolloServerPluginLandingPageLocalDefault({
      embed: true,
    }),
  ],
  uploads: false, // Disable Apollo’s default upload handling if on NestJS 9+ (and Apollo Server version 10 and above).
  autoSchemaFile: 'src/schema.gql', // Adjust if your path differs!
  sortSchema: true,
  introspection: true,
  context: ({ req, res }) =&amp;gt; ({ req, res }),
})
// ...other imports
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then configure the &lt;code&gt;graphqlUploadExpress&lt;/code&gt; middleware within the same &lt;code&gt;app.module.ts&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;export class GraphQLAppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(
      // apply the upload middleware, setting reasonable defaults.
        graphqlUploadExpress({
          maxFieldSize:100 * 1024 * 1024, // 100MB
          maxFileSize: 100 * 1024 * 1024, // 100MB
          maxFiles: 10, // 10 files per request
        })
      )
      .forRoutes('graphql');
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;for-the-bare-metal-express-server-category-&quot;&gt;for the bare-metal express server category:&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const express = require('express');
const expressGraphql = require('express-graphql');
const { graphqlUploadExpress } = require('graphql-upload-ts');

express()
  .use(
    '/graphql',
    // apply the upload middleware, setting reasonable defaults.
    graphqlUploadExpress({
      maxFieldSize:100 * 1024 * 1024, // 100MB
      maxFileSize: 100 * 1024 * 1024, // 100MB
      maxFiles: 10, // 10 files per request
    }),
    // Replace with your schema path
    expressGraphql({ schema: require('./schema.gql') })
  )
  .listen(3000);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;2-server-models-dtos-amp-resolvers-building-your-upload-endpoint&quot;&gt;2. Server Models, DTOs, &amp;amp; Resolvers: Building Your Upload Endpoint&lt;/h4&gt;
&lt;p&gt;Next, we'll define our model, which we'll call &lt;code&gt;BigFileDataModel&lt;/code&gt;.&lt;br&gt;
For nestjs users, I'm using the code-first approach. For the rest of the readers, I'll also include a snippet of the resulting schema to include in your schema files.&lt;/p&gt;
&lt;p&gt;The typescript definition:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { Field, InputType } from '@nestjs/graphql';
import { FileUpload, GraphQLUpload } from 'graphql-upload-ts';
import { IsIn, IsNotEmpty } from 'class-validator';

@InputType()
export class BigFileDataModel {
  @Field()
  @IsNotEmpty()
  @IsIn(['mp4', 'mov', 'avi', 'mkv', 'webp', 'jpeg', 'jpg', 'png'])
  fileType: string;

  @Field(() =&amp;gt; GraphQLUpload)
  contents: Promise&amp;lt;FileUpload&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The equivalent schema:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphql&quot;&gt;&quot;&quot;&quot;The `Upload` scalar type represents a file upload.&quot;&quot;&quot;
scalar Upload

input BigFileDataModel {
  contents: Upload!
  fileType: String!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our &lt;code&gt;BigFileDataModel&lt;/code&gt; has a file &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;contents&lt;/code&gt;. The fileType is the file extension (so &lt;code&gt;.png&lt;/code&gt;, &lt;code&gt;.jpg&lt;/code&gt; and so on), and the contents is the binary file itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A quick thing to note: Make sure the data types match the typescript example if you are using schema-first approach, specifically the contents (which will be the file being streamed), which returns a promise when all the file bytes have finished transmitting.&lt;/strong&gt;&lt;/p&gt;
&lt;h5 id=&quot;request-amp-response-dtos&quot;&gt;Request &amp;amp; Response DTOs&lt;/h5&gt;
&lt;p&gt;Next, we'll create our DTOs that will be used by a resolver (which we'll create in a future step).&lt;br&gt;
The DTOs will be basically a request and response DTO (Data Transfer Object).&lt;br&gt;
For nestjs readers using the code-first approach, here's the TypeScript definitions.&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;Request&lt;/code&gt; DTO:&lt;br&gt;
&lt;strong&gt;Request DTO:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { ArgsType, Field } from '@nestjs/graphql';

@ArgsType()
export class UploadFileRequest {
  @Field(() =&amp;gt; BigFileDataModel)
  file: BigFileDataModel;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Response DTO:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { Field, ObjectType } from '@nestjs/graphql';

@ObjectType()
export class _UploadFileResponse {
  @Field({ nullable: true })
  file: string;
}

// Use your preferred base response structure, here I'm assuming inheritance/mixins:
@ObjectType()
export class UploadFileResponse extends StandardGraphQLResponse(_UploadFileResponse) {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For our schema-first readers, here's the equivalent schema:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphql&quot;&gt;&quot;&quot;&quot;The `Upload` scalar type represents a file upload.&quot;&quot;&quot;
scalar Upload

input BigFileDataModel {
  contents: Upload!
  fileType: String!
}

&quot;&quot;&quot;The section above has been included from a previous step for consistency &quot;&quot;&quot;

type UploadFileResponse {
  file: String!
}

type Mutation {
  uploadFile(file: BigFileDataModel!): UploadFileResponse!
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The request DTO basically takes an argument &lt;code&gt;file&lt;/code&gt; which specifies the file properties we are sending from the client.&lt;/p&gt;
&lt;h5 id=&quot;our-resolver-and-fileuploadservice-class&quot;&gt;Our Resolver and FileUploadService class&lt;/h5&gt;
&lt;p&gt;At this point I don't think I need a 'nestjs code-first approach users' and 'schema-first users'. I assume you'll be smart enough to deduce which code snippet is for you moving forward.&lt;/p&gt;
&lt;p&gt;Our typescript resolver (feel free to adapt this without the annotations to your resolver definition):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { Resolver, Mutation, Args } from '@nestjs/graphql';

@Resolver('FileUploadResolver')
export class FileUploadResolver {
  constructor(private readonly fileUploadService: FileUploadService) {}

  @Mutation(() =&amp;gt; UploadFileResponse)
  async uploadFile(@Args() req: UploadFileRequest) {
    const res = await this.fileUploadService.uploadToFirebase({
      file: req.file.contents,
      fileType: req.file.fileType,
    });
    return { file: res };
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resolver basically calls our file upload service (which uploads the binary file to Google Storage, and returns a string representing the public url).&lt;/p&gt;
&lt;p&gt;Here's the definition of our &lt;code&gt;FileUploadService.ts&lt;/code&gt;, which you can feel free to re-use in your personal services requiring file upload.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { Injectable } from '@nestjs/common';
import { Readable } from 'stream';
import { FileUpload } from 'graphql-upload-ts';

@Injectable()
export class FileUploadService {
  constructor(private readonly folderName: string) {}

  async uploadToFirebase({
    file,
    fileType,
  }: {
    file: Promise&amp;lt;FileUpload&amp;gt;;
    fileType?: string;
  }): Promise&amp;lt;string&amp;gt; {
    const buffer = await this.streamToBuffer((await file).createReadStream());
    const _fileName = `${Date.now()}-${Math.round(Math.random() * 1e9)}.${fileType}`;
    
    // Check the definition for this method below
    return await this.saveFileToStorage(this.folderName, _fileName, buffer);
  }

  private async streamToBuffer(readableStream: Readable): Promise&amp;lt;Buffer&amp;gt; {
    return new Promise((resolve, reject) =&amp;gt; {
      const chunks: Buffer[] = [];
      readableStream.on('data', (data) =&amp;gt; {
        if (typeof data === 'string') {
          chunks.push(Buffer.from(data, 'utf-8'));
        } else {
          chunks.push(data);
        }
      });
      readableStream.on('end', () =&amp;gt; resolve(Buffer.concat(chunks)));
      readableStream.on('error', reject);
    });
  }

  // Stub: wire this up to your favorite storage backend
  private async saveFileToStorage(folder: string, fileName: string, buffer: Buffer): Promise&amp;lt;string&amp;gt; {
    // Implementation left as an exercise for the reader. Feel free to user Google storage/S3/local-fs or your own implementation for this.
    return `https://storage.example.com/${folder}/${fileName}`;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you have it - we have our server set up and ready to receive graphql file uploads. You can stop here if you're a just-backend engineer and hate anything to do with HTML/CSS/JavaScript.&lt;/p&gt;
&lt;p&gt;For our full-stack engineers (yes, you rank higher than the just-backend engineers) who also want to set up a client, read on.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;3-client-side-wizardry-uploading-files-with-apollo-client&quot;&gt;3. Client-Side Wizardry: Uploading Files with Apollo Client&lt;/h4&gt;
&lt;p&gt;Let's face it: JavaScript frontend frameworks multiply like rabbits. As a result, I'll try my best to keep the client implementation as &lt;em&gt;framework-agnostic&lt;/em&gt; as possible. I'll use Apollo Client as an agnostic toolkit (since it's a separate implementation that does not depend on any frameworks, and is in the blog title).&lt;/p&gt;
&lt;h5 id=&quot;install-necessary-dependencies-&quot;&gt;Install necessary dependencies:&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install @apollo/client apollo-upload-client
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;setup-apollo-client-setup-with-upload-&quot;&gt;Setup Apollo Client Setup (with Upload)&lt;/h5&gt;
&lt;p&gt;Set up your Apollo Client (which you can use in your ApolloProvider in the case of Next.js. For other frameworks, you're on your own here):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { ApolloClient, InMemoryCache } from '@apollo/client';
import { createUploadLink } from 'apollo-upload-client';
import { setContext } from '@apollo/client/link/context';

const httpLink = createUploadLink({
  uri: 'http://localhost:4000/graphql', // Plug in your server URL!
});

const headersLink = setContext((_, { headers }) =&amp;gt; ({
  headers: {
    ...headers,
    'Apollo-Require-Preflight': 'true', // Enable CORS for file uploads
  },
}));

const apolloClient = new ApolloClient({
  cache: new InMemoryCache(),
  link: headersLink.concat(httpLink),
  ssrMode: typeof window === 'undefined', // Server-side support, if needed
});

export default apolloClient;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, we create two links: one for injecting headers, and the other, a &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.apollographql.com/docs/react/api/link/introduction/#the-terminating-link&quot;&gt;http terminating link&lt;/a&gt; for file upload, provided by the &lt;code&gt;apollo-upload-client&lt;/code&gt; package.&lt;br&gt;
Then we include the &lt;code&gt; 'Apollo-Require-Preflight': 'true'&lt;/code&gt; header, which is needed for enabling cors. This is outside the scope of this tutorial, but if you'd like to find out more, you &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.apollographql.com/docs/apollo-server/security/cors#graphql-upload&quot;&gt;can here&lt;/a&gt;;&lt;/p&gt;
&lt;h5 id=&quot;the-graphql-mutation&quot;&gt;The GraphQL Mutation&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphql&quot;&gt;mutation uploadFile($file: BigFileDataModel!) {
  uploadFile(file: $file) {
    file
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;file-input-and-upload-logic&quot;&gt;File Input and Upload Logic&lt;/h5&gt;
&lt;p&gt;Here’s a barebones React-flavored file uploader. You can adapt this for your front-end framework of choice.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { useMutation } from '@apollo/client';
const MUTATION = /* see mutation above */;

function UploadFile() {
  const [mutate] = useMutation(MUTATION);

  return (
    &amp;lt;input
      type=&quot;file&quot;
      required
      onChange={({
        target: {
          validity,
          files: [file],
        },
      }) =&amp;gt; {
        if (validity.valid)
          mutate({
            variables: {
              file,
            },
          });
      }}
    /&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;i-hope-the-code-snippets-above-are-self-explanatory-considering-this-is-applicable-to-all-frontend-frameworks-if-you-run-into-any-issues-always-feel-free-to-tag-me-in-the-comment-section-and-i-ll-respond-as-soon-as-i-can-&quot;&gt;I hope the code snippets above are self-explanatory (considering this is applicable to all frontend frameworks). If you run into any issues, always feel free to tag me in the comment section, and I'll respond as soon as I can!&lt;/h3&gt;
&lt;h4 id=&quot;wrapping-up-&quot;&gt;Wrapping Up...&lt;/h4&gt;
&lt;p&gt;You should now have a working, end-to-end GraphQL file upload system, complete with server, DTOs, middleware, resolvers, upload streaming, Apollo Client, and a front-end implementation.&lt;br&gt;
If there's a framework or storage backend I missed, or you have a better implementation, drop your thoughts in the comments! &lt;br&gt;
Until next time, happy hacking!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Node + TS</category>
<guid isPermaLink="true">https://coderlegion.com/5100/end-to-end-graphql-file-uploads-with-apollo-server-and-apollo-client-comprehensive-guide</guid>
<pubDate>Sat, 30 Aug 2025 11:10:34 +0000</pubDate>
</item>
<item>
<title>From Basics to Best Practices: Why Angular Developers Should Adopt GraphQL</title>
<link>https://coderlegion.com/3372/from-basics-to-best-practices-why-angular-developers-should-adopt-graphql</link>
<description>&lt;hr&gt;
&lt;p&gt;As Angular developers, we’re used to structured development, powerful CLI tools, and TypeScript's safety. But when it comes to APIs, most of us still work with REST. While REST has served us well, &lt;strong&gt;GraphQL&lt;/strong&gt; offers a modern, more flexible alternative—especially when building &lt;strong&gt;dynamic, scalable Angular applications&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this blog, we’ll explore why GraphQL fits naturally into Angular projects, how to get started, and what best practices to follow.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-should-angular-developers-care-about-graphql-&quot;&gt;Why Should Angular Developers Care About GraphQL?&lt;/h3&gt;
&lt;h4 id=&quot;1-strong-typing-typescript-s-best-friend&quot;&gt;1. Strong Typing = TypeScript's Best Friend&lt;/h4&gt;
&lt;p&gt;GraphQL uses a &lt;strong&gt;strongly-typed schema&lt;/strong&gt;, which integrates perfectly with Angular's TypeScript-first ecosystem. With tools like &lt;a rel=&quot;nofollow&quot; href=&quot;https://the-guild.dev/graphql/codegen&quot;&gt;GraphQL Code Generator&lt;/a&gt;, you can auto-generate TypeScript types for your queries, reducing errors and boosting DX (developer experience).&lt;/p&gt;
&lt;h4 id=&quot;2-single-endpoint-precise-queries&quot;&gt;2. Single Endpoint, Precise Queries&lt;/h4&gt;
&lt;p&gt;Instead of juggling multiple REST endpoints, GraphQL uses &lt;strong&gt;one endpoint&lt;/strong&gt; and lets clients ask for exactly what they need. This avoids over-fetching/under-fetching and speeds up UI development in Angular components.&lt;/p&gt;
&lt;h4 id=&quot;3-better-for-complex-uis&quot;&gt;3. Better for Complex UIs&lt;/h4&gt;
&lt;p&gt;GraphQL’s nested queries let you fetch related data in one go—ideal for component-driven UIs in Angular, where you need parent and child data together.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;setting-up-graphql-in-an-angular-project&quot;&gt;Setting Up GraphQL in an Angular Project&lt;/h3&gt;
&lt;h4 id=&quot;step-1-install-apollo-client&quot;&gt;Step 1: Install Apollo Client&lt;/h4&gt;
&lt;p&gt;Use &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.apollographql.com/docs/angular/&quot;&gt;Apollo Angular&lt;/a&gt; to integrate GraphQL easily.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ng add apollo-angular
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the CLI command doesn’t work, install manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install @apollo/client graphql apollo-angular
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;step-2-configure-apollo-client&quot;&gt;Step 2: Configure Apollo Client&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// app.module.ts
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
import { InMemoryCache } from '@apollo/client/core';

@NgModule({
  imports: [HttpClientModule, ApolloModule],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: (httpLink: HttpLink) =&amp;gt; ({
        cache: new InMemoryCache(),
        link: httpLink.create({ uri: 'https://your-graphql-api.com/graphql' }),
      }),
      deps: [HttpLink],
    },
  ],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;step-3-create-a-query-in-your-component&quot;&gt;Step 3: Create a Query in Your Component&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// app.component.ts
import { Component } from '@angular/core';
import { Apollo, gql } from 'apollo-angular';

const GET_USERS = gql`
  query {
    users {
      id
      name
      email
    }
  }
`;

@Component({
  selector: 'app-root',
  template: `
    &amp;lt;h1&amp;gt;Users&amp;lt;/h1&amp;gt;
    &amp;lt;ul *ngIf=&quot;users&quot;&amp;gt;
      &amp;lt;li *ngFor=&quot;let user of users&quot;&amp;gt;{{ user.name }} ({{ user.email }})&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  `,
})
export class AppComponent {
  users: any[] = [];

  constructor(private apollo: Apollo) {
    this.apollo.watchQuery({ query: GET_USERS }).valueChanges.subscribe((result: any) =&amp;gt; {
      this.users = result?.data?.users || [];
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;bonus-auto-generate-types&quot;&gt;Bonus: Auto-Generate Types&lt;/h3&gt;
&lt;p&gt;Install GraphQL Code Generator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install @graphql-codegen/cli
npx graphql-codegen init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tool reads your schema and queries, and auto-generates TypeScript interfaces, query functions, and hooks for Angular.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;best-practices-for-angular-graphql&quot;&gt;Best Practices for Angular + GraphQL&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Practice                     &lt;/th&gt;&lt;th&gt;Why it matters&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;✅ Use GraphQL Codegen        &lt;/td&gt;&lt;td&gt;Ensures strict type safety and DX&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;✅ Use Apollo Caching         &lt;/td&gt;&lt;td&gt;Boosts performance with normalized caching&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;✅ Separate Queries/Mutations &lt;/td&gt;&lt;td&gt;Keep queries in &lt;code&gt;.graphql.ts&lt;/code&gt; or &lt;code&gt;.gql&lt;/code&gt; files&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;✅ Use Fragments              &lt;/td&gt;&lt;td&gt;Reuse common fields between components&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;✅ Use Interceptors           &lt;/td&gt;&lt;td&gt;Add auth tokens dynamically&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;✅ Paginate Large Lists       &lt;/td&gt;&lt;td&gt;Avoid loading unnecessary data&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;angular-amp-graphql-auth-tip&quot;&gt;Angular &amp;amp; GraphQL Auth Tip&lt;/h3&gt;
&lt;p&gt;To attach auth tokens to GraphQL requests, use an &lt;code&gt;ApolloLink&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { setContext } from '@apollo/client/link/context';

const authLink = setContext((_, { headers }) =&amp;gt; {
  const token = localStorage.getItem('token');
  return {
    headers: {
      ...headers,
      Authorization: token ? `Bearer ${token}` : '',
    },
  };
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then combine &lt;code&gt;authLink&lt;/code&gt; with &lt;code&gt;httpLink&lt;/code&gt; using &lt;code&gt;from&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;link: from([authLink, httpLink.create({ uri: '...' })])
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;summary-why-angular-devs-should-adopt-graphql&quot;&gt;Summary: Why Angular Devs Should Adopt GraphQL&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Benefit                  &lt;/th&gt;&lt;th&gt;Impact&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Typed queries            &lt;/td&gt;&lt;td&gt;TypeScript-friendly development&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Declarative data loading &lt;/td&gt;&lt;td&gt;Cleaner, more readable components&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Schema introspection     &lt;/td&gt;&lt;td&gt;Better tooling and autocompletion&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Single endpoint          &lt;/td&gt;&lt;td&gt;Less REST boilerplate, more flexibility&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ecosystem support        &lt;/td&gt;&lt;td&gt;Apollo, Codegen, DevTools, Angular CLI&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;useful-resources&quot;&gt;Useful Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.apollographql.com/docs/angular/&quot;&gt;Apollo Angular Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://the-guild.dev/graphql/codegen&quot;&gt;GraphQL Code Generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.graphqlbin.com/&quot;&gt;GraphQL Playground&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://graphql.org/learn/best-practices/&quot;&gt;GraphQL Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Ready to ditch the REST and supercharge your Angular app?&lt;/strong&gt; GraphQL isn't just a trend—it's a robust tool for building modern, efficient frontends. And in Angular’s TypeScript-powered world, it's a perfect match.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/3372/from-basics-to-best-practices-why-angular-developers-should-adopt-graphql</guid>
<pubDate>Fri, 20 Jun 2025 03:04:20 +0000</pubDate>
</item>
</channel>
</rss>