It took me a while to figure out how I can use GraphQL with the ent ORM and serve the GraphQL endpoint via the Echo framework

Go + Graphql + Ent + Echo Boilerplate

It took me a while to figure out how I can use GraphQL with the ent ORM and serve the GraphQL endpoint via the Echo framework.

I also wanted proper configuration management and a clean structure for my application logic. It also should be easily testable, because I only write tests if it's convenient.

That's why I came up with Mega which could stand for something like "Merge ent with GraphQL awesomeness" or "My earliest Go adventures".

What's inside the box

The challenge was to glue everything as modular as possible together to make replacing single components easy. I'm also not a fan of reflection, so the used libraries like ent, gqlgen, and Wire generate the necessary code instead. It adds little overhead, as you always have to run make ent|graphql|wire after each modification. But it adds a lot of type safety and makes developing so much easier.

Give me some structure

One of the advantages of Go is its flexibility when it comes to structuring your application. It could also be challenging and requires continuous refactoring as your application grows.

I've worked on several web applications using Go in the past and tried a lot of different approaches. From throwing everything in one package (hey my PHP friends from the past) to making anything as modular as possible (waving over to the Java guys).

I've tried clean architecture and also domain-driven design. Both have pros and cons, but they felt too overkill for most of my Go projects. So I've ended up with the idea of encapsulating my application logic into small services that have a well-defined interface, are easily testable, and can be used with a variety of API frontends like REST, GraphQL, or gRPC.

Services to the rescue

All my application logic is contained in different services. Each service provides an interface to describe its API. Each service should but must not contain a test suite.

If you have a closer look at a service, you could see the following components:

type Service interface {
	Get(ctx context.Context, id uuid.UUID) (*ent.User, error)
	Create(ctx context.Context, user model.AddUserInput) (*ent.User, error)
}

The Service interface defines all the offered methods. It also helps to decouple your code if you reference via interfaces instead of specific types.

type User struct {
	client *db.Client
}

The User struct bundles all service methods together and contains the needed dependencies for the service. If you need to send emails, you could, for example, inject a mailer dependency that provides functionality to send mails.

func New(client *db.Client) Service {
	return &User{client: client}
}

The New method simply acts as an initializer.

func (s *User) Get(ctx context.Context, id uuid.UUID) (*ent.User, error) {
	return s.client.User.Get(ctx, id)
}

And last but not least, the Get method on the User struct is the actual application logic.

Getting started

I've decided to use make for automating all the tasks. That's why all commands can be run via make.

  • make ent - Generates the required code from your defined models.
  • make graphql - Generates the GraphQL resolvers and models from the schema.
  • make wire - Generates the dependency injection code.
  • make all - Executes the ent, graphql, and wire task all together.
  • make run - Starts the web server.
  • make test - Executes all tests.

After each modification of the GraphQL schema, the models or the dependency graph you need to run either make ent|graphql|wire or make all.

Defining your GraphQl schema

The schema is located under the /graph/schema directory. It contains two different types of files. The "main" schema in schema.graphqls contains common types like a Timestamp. And the service-specific schemas like user.graphqls contain the query and mutation definitions for the respective service. Splitting your schema into multiple files also makes it much easier to implement the resolvers later, as they only contain your service-specific queries and mutations and not those of the whole application.

Configuration

As I'm using Viper as configuration management, it is easy to have specific configurations for different environments. Just copy the config.yaml in the repository's root and adapt the setting for a different environment.

The default make run command takes the default config file named config.yml in the repo's root.

To use your own config file, simply supply the -config mynewconfig.yml argument to the go run command.

go run cmd/main.go -config dev.yml

First steps

If you execute make run, it will start the server on http://127.0.0.1:8080. The default GraphQL query endpoint is under 127.0.0.1:8080/query. There is also a GraphQL playground which is available via 127.0.0.1:8080/playground.

To insert your first user, simply run the following mutation:

mutation {
  createUser(user: {name: "Foo", age: 10}) {
    id,
    createdAt
  }
}

It will return the following response:

{
  "data": {
    "createUser": {
      "id": "da612c7d-494c-4164-9afc-d353c3e923cf",
      "createdAt": "2022-01-24T00:09:11+01:00"
    }
  }
}

You can see a new user with the ID da612c7d-494c-4164-9afc-d353c3e923cf is created.

To query for that user, you can use the following query:

query {
  user(id: "39b066c7-4b34-4a41-9d21-0b55b16ff0eb") {
    id
    createdAt
  }
}

And it returns the same response as the mutation above:

{
  "data": {
    "user": {
      "id": "39b066c7-4b34-4a41-9d21-0b55b16ff0eb",
      "createdAt": "2022-01-24T00:17:02+01:00"
    }
  }
}
Owner
Marc Boeker
Code MacGyver
Marc Boeker
Similar Resources

EchoMiddleware - Echo Middleware with golang

EchoMiddleware middleware for echo server usage import ( "github.com/universe-3

Jan 4, 2022

Go Fiber Boilerplate with Gorm ORM

Go Fiber Boilerplate with Gorm ORM This boilerplate app is using Go version 1.17 because I think for now this is the most updated release. Installatio

Jul 30, 2022

REST api using fiber framework written in golang and using firebase ecosystem to authentication, storage and firestore as a db and use clean architecture as base

REST api using fiber framework written in golang and using firebase ecosystem to authentication, storage and firestore as a db and use clean architecture as base

Backend API Example FiberGo Framework Docs : https://github.com/gofiber Info This application using firebase ecosystem Firebase Auth Cloud Storage Fir

May 31, 2022

Flamingo Framework and Core Library. Flamingo is a go based framework for pluggable web projects. It is used to build scalable and maintainable (web)applications.

Flamingo Framework and Core Library. Flamingo is a go based framework for pluggable web projects. It is used to build scalable and maintainable (web)applications.

Flamingo Framework Flamingo is a web framework based on Go. It is designed to build pluggable and maintainable web projects. It is production ready, f

Jan 5, 2023

An efficient, extensible and easy-to-use RPC framework.

An efficient, extensible and easy-to-use RPC framework.

eRPC eRPC is an efficient, extensible and easy-to-use RPC framework. Suitable for RPC, Microservice, Peer-to-Peer, IM, Game and other fields. 简体中文 Ins

Dec 29, 2022

lazy-go is an easy-to-use WEB framework.

lazy-go lazy-go is an easy-to-use WEB framework. Installation Run the following command under your project: go get -u github.com/NICEXAI/lazy-go Quick

Mar 25, 2022

Golanger Web Framework is a lightweight framework for writing web applications in Go.

/* Copyright 2013 Golanger.com. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except

Nov 14, 2022

laravel for golang,goal,fullstack framework,api framework

laravel for golang,goal,fullstack framework,api framework

laravel for golang,goal,fullstack framework,api framework

Feb 24, 2022
Comments
  • Bump github.com/labstack/echo/v4 from 4.6.1 to 4.9.0

    Bump github.com/labstack/echo/v4 from 4.6.1 to 4.9.0

    Bumps github.com/labstack/echo/v4 from 4.6.1 to 4.9.0.

    Release notes

    Sourced from github.com/labstack/echo/v4's releases.

    v4.9.0

    Security

    • Fix open redirect vulnerability in handlers serving static directories (e.Static, e.StaticFs, echo.StaticDirectoryHandler) #2260

    Enhancements

    • Allow configuring ErrorHandler in CSRF middleware #2257
    • Replace HTTP method constants in tests with stdlib constants #2247

    v4.8.0

    Most notable things

    You can now add any arbitrary HTTP method type as a route #2237

    e.Add("COPY", "/*", func(c echo.Context) error 
      return c.String(http.StatusOK, "OK COPY")
    })
    

    You can add custom 404 handler for specific paths #2217

    e.RouteNotFound("/*", func(c echo.Context) error { return c.NoContent(http.StatusNotFound) })
    

    g := e.Group("/images") g.RouteNotFound("/*", func(c echo.Context) error { return c.NoContent(http.StatusNotFound) })

    Enhancements

    • Add new value binding methods (UnixTimeMilli,TextUnmarshaler,JSONUnmarshaler) to Valuebinder #2127
    • Refactor: body_limit middleware unit test #2145
    • Refactor: Timeout mw: rework how test waits for timeout. #2187
    • BasicAuth middleware returns 500 InternalServerError on invalid base64 strings but should return 400 #2191
    • Refactor: duplicated findStaticChild process at findChildWithLabel #2176
    • Allow different param names in different methods with same path scheme #2209
    • Add support for registering handlers for different 404 routes #2217
    • Middlewares should use errors.As() instead of type assertion on HTTPError #2227
    • Allow arbitrary HTTP method types to be added as routes #2237

    v4.7.2

    Fixes

    • Fix nil pointer exception when calling Start again after address binding error #2131
    • Fix CSRF middleware not being able to extract token from multipart/form-data form #2136
    • Fix Timeout middleware write race #2126

    Enhancements

    ... (truncated)

    Changelog

    Sourced from github.com/labstack/echo/v4's changelog.

    v4.9.0 - 2022-09-04

    Security

    • Fix open redirect vulnerability in handlers serving static directories (e.Static, e.StaticFs, echo.StaticDirectoryHandler) #2260

    Enhancements

    • Allow configuring ErrorHandler in CSRF middleware #2257
    • Replace HTTP method constants in tests with stdlib constants #2247

    v4.8.0 - 2022-08-10

    Most notable things

    You can now add any arbitrary HTTP method type as a route #2237

    e.Add("COPY", "/*", func(c echo.Context) error 
      return c.String(http.StatusOK, "OK COPY")
    })
    

    You can add custom 404 handler for specific paths #2217

    e.RouteNotFound("/*", func(c echo.Context) error { return c.NoContent(http.StatusNotFound) })
    

    g := e.Group("/images") g.RouteNotFound("/*", func(c echo.Context) error { return c.NoContent(http.StatusNotFound) })

    Enhancements

    • Add new value binding methods (UnixTimeMilli,TextUnmarshaler,JSONUnmarshaler) to Valuebinder #2127
    • Refactor: body_limit middleware unit test #2145
    • Refactor: Timeout mw: rework how test waits for timeout. #2187
    • BasicAuth middleware returns 500 InternalServerError on invalid base64 strings but should return 400 #2191
    • Refactor: duplicated findStaticChild process at findChildWithLabel #2176
    • Allow different param names in different methods with same path scheme #2209
    • Add support for registering handlers for different 404 routes #2217
    • Middlewares should use errors.As() instead of type assertion on HTTPError #2227
    • Allow arbitrary HTTP method types to be added as routes #2237

    v4.7.2 - 2022-03-16

    Fixes

    • Fix nil pointer exception when calling Start again after address binding error #2131
    • Fix CSRF middleware not being able to extract token from multipart/form-data form #2136
    • Fix Timeout middleware write race #2126

    ... (truncated)

    Commits
    • 16d3b65 Changelog for 4.9.0
    • 0ac4d74 Fix #2259 open redirect vulnerability in echo.StaticDirectoryHandler (used by...
    • d77e8c0 Added ErrorHandler and ErrorHandlerWithContext in CSRF middleware (#2257)
    • 534bbb8 replace POST constance with stdlib constance
    • fb57d96 replace GET constance with stdlib constance
    • d48197d Changelog for 4.8.0
    • cba12a5 Allow arbitrary HTTP method types to be added as routes
    • a327884 add:README.md-Third-party middlewares-github.com/go-woo/protoc-gen-echo
    • 61422dd Update CI-flow (Go 1.19 +deps)
    • a9879ff Middlewares should use errors.As() instead of type assertion on HTTPError
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

Bootstrapper and middleware for Echo framework in golang.
Bootstrapper and middleware for Echo framework in golang.

rk-echo Interceptor & bootstrapper designed for echo framework. Currently, supports bellow functionalities. Name Description Start with YAML Start ser

Jul 9, 2022
Example Golang API backend rest implementation mini project Point Of Sale using Gin Framework and Gorm ORM Database.

Example Golang API backend rest implementation mini project Point Of Sale using Gin Framework and Gorm ORM Database.

Dec 23, 2022
Golang Echo Framework

template-golang-echo Creator: 한승진 Date: 2021/10/13 Description Kubernetes-Istio 환경인 리얼패킹 클라우드에 배포하기 위한 Golang Echo 서버 기초 템플릿입니다. 배포 타겟 기술스텍 : Kubernet

Oct 13, 2021
REST API with Echo Framework from Go
REST API with Echo Framework from Go

REST API with Echo Framework from Go

Nov 16, 2021
A Golang restful API boilerplate based on Echo framework v4

A Golang restful API boilerplate based on Echo framework v4. Includes tools for module generation, db migration, authorization, authentication and more.

Nov 15, 2022
Simple go endpoint with gorilla / mux routing, testing, dockerfile

Simple go endpoint with gorilla / mux routing, testing, dockerfile

Feb 1, 2022
The jin is a simplified version of the gin web framework that can help you quickly understand the core principles of a web framework.

jin About The jin is a simplified version of the gin web framework that can help you quickly understand the core principles of a web framework. If thi

Jul 14, 2022
CRUD API server of Clean Architecture with Go(Echo), Gorm, MySQL, Docker and Swagger
CRUD API server of Clean Architecture with Go(Echo), Gorm, MySQL, Docker and Swagger

CRUD API Server of Clean Architecture Go(echo) gorm mysql docker swagger build docker-compose up -d --build API Postman and Fiddler is recommended to

May 14, 2022
Simple REST-API implementation using Golang with several packages (Echo, GORM) and Docker

Simple REST-API Boilerplate This is a simple implementation of REST-API using Golang and several packages (Echo and GORM). By default, I use PostgreSQ

Sep 13, 2022
Auth0 Middleware for go labstack/echo

go-echo-auth0-middleware Auth0 Middleware for go labstack/echo Example package main import ( "net/http" "github.com/auth0/go-jwt-middleware/v2/val

Nov 24, 2022