Skip to content

dguisinger/lambda-graphql

Repository files navigation

Lambda.GraphQL

NuGet Build Status License

A .NET library that generates GraphQL schemas from C# Lambda functions for AWS AppSync. Provides compile-time schema generation through source generators and MSBuild tasks, enabling type-safe GraphQL API development with AWS Lambda Annotations.

✨ Features

  • πŸ”§ Compile-Time Generation - GraphQL schemas generated during build with zero runtime overhead
  • πŸ›‘οΈ Type Safety - Full C# type safety with GraphQL schema validation and IntelliSense
  • ☁️ AWS Native - Built specifically for AWS AppSync with CDK integration
  • πŸš€ Advanced GraphQL - Union types, interfaces, directives, subscriptions, and custom scalars
  • πŸ“¦ Zero Dependencies - Pure compile-time source generation with no runtime dependencies
  • πŸ”„ AOT Compatible - Works seamlessly with Native AOT compilation

πŸš€ Quick Start (Hackathon Evaluation)

Prerequisites

  • .NET 6.0+ SDK
  • Git

Setup for Evaluation

# Clone the repository
git clone https://github.com/dguisinger/lambda-graphql
cd lambda-graphql

# Build the solution
dotnet build

# Run tests to verify functionality
dotnet test

# See the generated schema and resolvers
cat Lambda.GraphQL.Examples/schema.graphql
cat Lambda.GraphQL.Examples/resolvers.json

# See the generated C# source files (optional)
find Lambda.GraphQL.Examples/obj -name "*.cs" -path "*GraphQLSchemaGenerator*"

Try It Yourself

# Create a new test project
dotnet new console -n MyGraphQLTest
cd MyGraphQLTest

# Add project reference (instead of NuGet package)
dotnet add reference ../Lambda.GraphQL/Lambda.GraphQL.csproj
dotnet add package Amazon.Lambda.Annotations

# Copy example code from Lambda.GraphQL.Examples
# Build and see your schema generated!
dotnet build

Note: This is a hackathon submission - package will be published to NuGet after evaluation.

Basic Usage

using Lambda.GraphQL.Attributes;
using Amazon.Lambda.Annotations;

// 1. Define your GraphQL types
[GraphQLType("Product", Description = "A product in the catalog")]
public class Product
{
    [GraphQLField(Description = "Unique product identifier")]
    public Guid Id { get; set; }
    
    [GraphQLField(Description = "Product name")]
    public string Name { get; set; }
    
    [GraphQLField(Description = "Product price in USD")]
    public decimal Price { get; set; }
}

// 2. Create input types
[GraphQLType("CreateProductInput", Kind = GraphQLTypeKind.Input)]
public class CreateProductInput
{
    [GraphQLField(Description = "Product name")]
    public string Name { get; set; }
    
    [GraphQLField(Description = "Product price")]
    public decimal Price { get; set; }
}

// 3. Implement Lambda functions with GraphQL operations
public class ProductFunctions
{
    [LambdaFunction]
    [GraphQLQuery("getProduct", Description = "Get a product by ID")]
    [GraphQLResolver(DataSource = "ProductsLambda")]
    public async Task<Product> GetProduct(
        [GraphQLArgument(Description = "Product ID")] Guid id)
    {
        // Your implementation here
        return new Product 
        { 
            Id = id, 
            Name = "Sample Product", 
            Price = 29.99m 
        };
    }

    [LambdaFunction]
    [GraphQLMutation("createProduct", Description = "Create a new product")]
    [GraphQLResolver(DataSource = "ProductsLambda")]
    public async Task<Product> CreateProduct(
        [GraphQLArgument] CreateProductInput input)
    {
        // Your implementation here
        return new Product 
        { 
            Id = Guid.NewGuid(), 
            Name = input.Name, 
            Price = input.Price 
        };
    }
}

Generated Output

The above code automatically generates:

schema.graphql

"""
Generated GraphQL schema from Lambda functions
"""
schema {
  query: Query
  mutation: Mutation
}

"""
A product in the catalog
"""
type Product {
  """
  Unique product identifier
  """
  id: ID!
  """
  Product name
  """
  name: String!
  """
  Product price in USD
  """
  price: Float!
}

input CreateProductInput {
  """
  Product name
  """
  name: String!
  """
  Product price
  """
  price: Float!
}

type Query {
  """
  Get a product by ID
  """
  getProduct(id: ID!): Product!
}

type Mutation {
  """
  Create a new product
  """
  createProduct(input: CreateProductInput!): Product!
}

resolvers.json

{
  "resolvers": [
    {
      "typeName": "Query",
      "fieldName": "getProduct",
      "kind": "UNIT",
      "dataSource": "ProductsLambda",
      "lambdaFunctionName": "GetProduct"
    },
    {
      "typeName": "Mutation", 
      "fieldName": "createProduct",
      "kind": "UNIT",
      "dataSource": "ProductsLambda",
      "lambdaFunctionName": "CreateProduct"
    }
  ],
  "dataSources": [
    {
      "name": "ProductsLambda",
      "type": "AWS_LAMBDA"
    }
  ]
}

🎯 Advanced Features

Union Types

[GraphQLUnion("SearchResult", "Product", "User", "Order")]
public class SearchResult { }

[LambdaFunction]
[GraphQLQuery("search")]
public async Task<List<object>> Search(string term)
{
    // Return mixed types - AppSync handles union resolution
    return results;
}

AWS Scalar Types

[GraphQLType("User")]
public class User
{
    [GraphQLField] public Guid Id { get; set; }           // β†’ ID!
    [GraphQLField] public DateTime CreatedAt { get; set; } // β†’ AWSDateTime!
    [GraphQLField] public DateOnly BirthDate { get; set; } // β†’ AWSDate
    [GraphQLField] public System.Net.Mail.MailAddress Email { get; set; } // β†’ AWSEmail!
    
    [GraphQLField]
    [GraphQLTimestamp]
    public long LastLoginTimestamp { get; set; }           // β†’ AWSTimestamp!
}

Subscriptions

[LambdaFunction]
[GraphQLSubscription("orderStatusChanged")]
[GraphQLResolver(DataSource = "OrderSubscriptionLambda")]
public async Task<Order> OrderStatusChanged(Guid orderId)
{
    // Subscription implementation
}

Auth Directives

[GraphQLType("AdminData")]
[GraphQLAuthDirective(AuthMode.UserPools, CognitoGroups = "admin")]
public class AdminData
{
    [GraphQLField] public string SensitiveInfo { get; set; }
}

πŸ“– Documentation

πŸ› οΈ Requirements

  • .NET 6.0+ - For source generator support
  • C# 10+ - For nullable reference types and modern language features
  • AWS Lambda Annotations - For Lambda function definitions

πŸ“¦ Packages

  • Lambda.GraphQL - Main package with attributes and source generator
  • Lambda.GraphQL.Build - MSBuild tasks for schema extraction
  • Lambda.GraphQL.SourceGenerator - Roslyn source generator (included automatically)

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for details on:

  • Setting up the development environment
  • Running tests and validation
  • Submitting pull requests
  • Code review process

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Built for the AWS AppSync and Lambda ecosystem
  • Inspired by GraphQL best practices and .NET source generator patterns
  • Developed as part of the Dynamous Kiro Hackathon

Ready to get started? Check out our Getting Started Guide or explore the Examples!

About

Dynamous Kiro Hackathon entry for AppSync GraphQL Schema gneration from C# Lambda Function code.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages