rocket

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2025 License: MIT Imports: 17 Imported by: 0

README

🚀 Rocket

Apollo GraphQL for Go - Bringing TypeScript GraphQL developer experience to Golang.

Philosophy

Rocket is inspired by Apollo GraphQL and aims to bring the same developer-friendly patterns to Go:

  • Schema-First: Define your API in .graphql files
  • Modular Resolvers: Each module owns its resolvers
  • Declarative: Map-based resolvers, no switch statements
  • Auto-Resolution: Struct fields auto-resolve, override only when needed
  • Field Order: Preserves query field selection order in responses
  • Type-Safe: Leverage Go's type system with minimal boilerplate

Features

  • TypeScript-like resolver pattern - Map-based resolvers you can spread/merge
  • 🎯 Auto-field resolution - Define custom resolvers only when you need them
  • 📦 Modular architecture - Each module is self-contained
  • 🔄 Schema compilation - Concat .graphql files with smart ordering
  • Hot reload support - Auto-recompile schemas on change
  • 🎨 Field order preservation - Responses match query field order
  • 🏗️ Built on Wundergraph - Production-grade GraphQL tools
  • 🎮 Apollo Sandbox - Modern playground with best-in-class DX

Quick Start

1. Define Your Schema
# src/user/schema.graphql
type User {
  id: ID!
  email: String!
  firstName: String!
  lastName: String!
}

extend type Query {
  user(id: ID!): User
  users(limit: Int): [User!]!
}
2. Create Module Resolvers
// src/user/resolvers.go
package user

import "github.com/jest-cloud/rocket"

type Resolvers struct {
    service *Service
}

func (r *Resolvers) QueryResolvers() map[string]rocket.FieldResolveFn {
    return map[string]rocket.FieldResolveFn{
        "user": func(p rocket.ResolveParams) (interface{}, error) {
            id := p.Args["id"].(string)
            return r.service.GetUserByID(p.Context, id)
        },
        "users": func(p rocket.ResolveParams) (interface{}, error) {
            limit := p.Args["limit"].(int)
            return r.service.GetAllUsers(p.Context, limit)
        },
    }
}

func (r *Resolvers) MutationResolvers() map[string]rocket.FieldResolveFn {
    return map[string]rocket.FieldResolveFn{}
}

func (r *Resolvers) TypeResolvers() map[string]map[string]rocket.FieldResolveFn {
    return map[string]map[string]rocket.FieldResolveFn{
        "User": {
            // Fields auto-resolve from User struct!
            // Only override when you need custom logic:
            "lastName": func(p rocket.ResolveParams) (interface{}, error) {
                user := p.Source.(*User)
                return strings.ToUpper(user.LastName), nil
            },
        },
    }
}
3. Build Schema
// main.go
import "github.com/jest-cloud/rocket"

func main() {
    // Initialize modules
    userModule := user.Initialize(db)
    orgModule := org.Initialize(db)
    
    // Build schema with all resolvers
    schema, err := rocket.BuildSchema(
        rocket.Config{
            SchemaPath: "schema/schema.graphql",
        },
        userModule.Resolvers,
        orgModule.Resolvers,
    )
    
    // Create HTTP handlers
    http.Handle("/graphql", rocket.Handler(schema))
    
    // Optional: Add playground
    http.HandleFunc("/playground", rocket.PlaygroundHandler("/graphql"))
    
    http.ListenAndServe(":8080", nil)
}

Comparison with TypeScript Apollo

TypeScript Apollo
const resolvers = {
  Query: {
    user: (parent, args, context) => {
      return userService.getUser(args.id);
    },
  },
  User: {
    // Fields auto-resolve!
    lastName: (parent) => parent.lastName.toUpperCase(),
  },
};
Rocket (Go)
func (r *Resolvers) QueryResolvers() map[string]rocket.FieldResolveFn {
    return map[string]rocket.FieldResolveFn{
        "user": func(p rocket.ResolveParams) (interface{}, error) {
            return r.service.GetUser(p.Args["id"].(string))
        },
    }
}

func (r *Resolvers) TypeResolvers() map[string]map[string]rocket.FieldResolveFn {
    return map[string]map[string]rocket.FieldResolveFn{
        "User": {
            // Fields auto-resolve!
            "lastName": func(p rocket.ResolveParams) (interface{}, error) {
                user := p.Source.(*User)
                return strings.ToUpper(user.LastName), nil
            },
        },
    }
}

Same pattern, same philosophy! 🎯

Architecture

Rocket Package
├── Schema Compiler      - Concatenates .graphql files
├── Schema Builder       - Parses and builds executable schema
├── Resolver Registry    - Stitches module resolvers together
├── Execution Engine     - Executes queries with field order
├── HTTP Handler         - Gin/net/http integration
└── Default Resolvers    - Auto-resolution for struct fields

Why Rocket?

  • 🚀 Fast to develop - Write less code, get more done
  • 🎯 Familiar patterns - If you know Apollo, you know Rocket
  • 💪 Production-ready - Built on Wundergraph's battle-tested tools
  • 🔧 Flexible - Override anything when you need to
  • 📦 Modular - Clean separation of concerns

Introspection Support

Rocket fully supports GraphQL introspection queries out of the box:

  • __schema - Get the schema structure
  • __type - Get information about a specific type
  • __typename - Get the type name of an object

The GraphQL Playground automatically uses introspection to provide autocomplete and documentation.

Coming Soon

  • Subscriptions support
  • DataLoader for N+1 prevention
  • Custom scalar types
  • Field-level middleware/directives
  • GraphQL Federation
  • Query complexity analysis

License

MIT


Rocket: Making GraphQL in Go feel like TypeScript Apollo 🚀

Documentation

Overview

Package rocket provides Apollo GraphQL-like patterns for Go Bringing TypeScript developer experience to Golang GraphQL development

Index

Constants

View Source
const (
	DefaultPreserveOrder = true
)

Config defaults

Variables

This section is empty.

Functions

func DefaultFieldResolver

func DefaultFieldResolver(p ResolveParams) (interface{}, error)

DefaultFieldResolver automatically resolves struct fields to GraphQL fields This is similar to Apollo's default field resolver in TypeScript It uses reflection to map GraphQL field names to Go struct fields

func Handler

func Handler(schema *Schema) http.HandlerFunc

Handler creates an HTTP handler for GraphQL requests Works with both net/http and Gin (via gin.WrapH)

func PlaygroundHandler

func PlaygroundHandler(endpoint string) http.HandlerFunc

PlaygroundHandler creates an HTTP handler that serves a GraphQL playground Uses Apollo Sandbox by default (most modern and stable)

func PlaygroundHandlerWithType

func PlaygroundHandlerWithType(endpoint string, playgroundType PlaygroundType) http.HandlerFunc

PlaygroundHandlerWithType creates a playground handler with a specific type

Types

type Config

type Config struct {
	SchemaPath       string // Path to the compiled schema.graphql file
	EnablePlayground bool   // Enable GraphQL playground (default: false)
}

Config holds configuration for building a GraphQL schema

type Error

type Error struct {
	Message string        `json:"message"`
	Path    []interface{} `json:"path,omitempty"`
}

Error represents a GraphQL error

type FieldResolveFn

type FieldResolveFn func(p ResolveParams) (interface{}, error)

FieldResolveFn is a function that resolves a field value

type ModuleResolvers

type ModuleResolvers interface {
	QueryResolvers() map[string]FieldResolveFn
	MutationResolvers() map[string]FieldResolveFn
	TypeResolvers() map[string]map[string]FieldResolveFn
}

ModuleResolvers is the interface that all module resolvers must implement This is similar to how you export resolvers in TypeScript Apollo

type OrderedMap

type OrderedMap struct {
	// contains filtered or unexported fields
}

OrderedMap preserves insertion order for JSON marshaling

func NewOrderedMap

func NewOrderedMap() *OrderedMap

NewOrderedMap creates a new ordered map

func (*OrderedMap) Get

func (m *OrderedMap) Get(key string) interface{}

Get retrieves a value by key

func (*OrderedMap) Has

func (m *OrderedMap) Has(key string) bool

Has checks if a key exists

func (*OrderedMap) Keys

func (m *OrderedMap) Keys() []string

Keys returns all keys in insertion order

func (*OrderedMap) MarshalJSON

func (m *OrderedMap) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler to preserve order

func (*OrderedMap) Set

func (m *OrderedMap) Set(key string, value interface{})

Set adds or updates a key-value pair

type PlaygroundType

type PlaygroundType string

PlaygroundType determines which playground interface to use

const (
	PlaygroundTypeApolloSandbox PlaygroundType = "apollo"     // Apollo Sandbox (recommended)
	PlaygroundTypeGraphiQL      PlaygroundType = "graphiql"   // GraphiQL (stable)
	PlaygroundTypePlayground    PlaygroundType = "playground" // GraphQL Playground (legacy)
)

type Request

type Request struct {
	Query         string                 `json:"query"`
	Variables     map[string]interface{} `json:"variables"`
	OperationName string                 `json:"operationName"`
}

Request represents a GraphQL HTTP request

type ResolveInfo

type ResolveInfo struct {
	FieldName    string
	Path         []string
	ParentType   string
	ReturnType   string
	SelectionSet interface{} // Will be wundergraph AST selection set
}

ResolveInfo contains metadata about the field being resolved

type ResolveParams

type ResolveParams struct {
	Source  interface{}
	Args    map[string]interface{}
	Context context.Context
	Info    ResolveInfo
}

ResolveParams contains all the information for resolving a field

type ResolverRegistry

type ResolverRegistry struct {
	Query    map[string]FieldResolveFn
	Mutation map[string]FieldResolveFn
	Types    map[string]map[string]FieldResolveFn
}

ResolverRegistry holds all resolvers stitched together Similar to how Apollo combines resolvers from different modules

func NewResolverRegistry

func NewResolverRegistry(modules ...ModuleResolvers) *ResolverRegistry

NewResolverRegistry creates a new registry by merging module resolvers This is like:

export const resolvers = {
  Query: { ...userQueries, ...orgQueries },
  Mutation: { ...userMutations, ...orgMutations }
}

func (*ResolverRegistry) GetMutationResolver

func (r *ResolverRegistry) GetMutationResolver(name string) (FieldResolveFn, bool)

GetMutationResolver returns a mutation resolver by name

func (*ResolverRegistry) GetQueryResolver

func (r *ResolverRegistry) GetQueryResolver(name string) (FieldResolveFn, bool)

GetQueryResolver returns a query resolver by name

func (*ResolverRegistry) GetTypeResolver

func (r *ResolverRegistry) GetTypeResolver(typeName, fieldName string) (FieldResolveFn, bool)

GetTypeResolver returns a type's field resolver

type Result

type Result struct {
	Data   interface{} `json:"data,omitempty"`
	Errors []Error     `json:"errors,omitempty"`
}

Result represents the result of a GraphQL operation

func ExecuteWithFieldOrder

func ExecuteWithFieldOrder(params graphql.Params, queryString string) *Result

ExecuteWithFieldOrder executes a GraphQL query and returns a result with preserved field order

type Schema

type Schema struct {
	// contains filtered or unexported fields
}

Schema represents a compiled and executable GraphQL schema

func BuildSchema

func BuildSchema(config Config, modules ...ModuleResolvers) (*Schema, error)

BuildSchema builds an executable GraphQL schema from .graphql file and module resolvers This is the main entry point for Rocket - similar to makeExecutableSchema in Apollo

func (*Schema) Execute

func (s *Schema) Execute(ctx context.Context, query string, variables map[string]interface{}, operationName string) *Result

Execute executes a GraphQL query/mutation Field order is always preserved (Apollo-like behavior)

type SchemaCompiler

type SchemaCompiler struct {
	// contains filtered or unexported fields
}

SchemaCompiler compiles multiple .graphql files into a single schema

func NewSchemaCompiler

func NewSchemaCompiler(sourceDir, outputFile string) *SchemaCompiler

NewSchemaCompiler creates a new schema compiler

func (*SchemaCompiler) Compile

func (c *SchemaCompiler) Compile() error

Compile finds all .graphql files and concatenates them into a single schema

func (*SchemaCompiler) CompileIfNeeded

func (c *SchemaCompiler) CompileIfNeeded() (bool, error)

CompileIfNeeded compiles the schema only if source files are newer than output

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL