Extensions

Introduction

The Ent Extension API facilitates the creation of code-generation extensions that bundle together codegen hooks, templates and annotations to create reusable components that add new rich functionality to Ent’s core. For example, Ent’s entgql plugin exposes an Extension that automatically generates GraphQL servers from an Ent schema.

Defining a New Extension

All extension’s must implement the Extension interface:

  1. type Extension interface {
  2. // Hooks holds an optional list of Hooks to apply
  3. // on the graph before/after the code-generation.
  4. Hooks() []gen.Hook
  5. // Annotations injects global annotations to the gen.Config object that
  6. // can be accessed globally in all templates. Unlike schema annotations,
  7. // being serializable to JSON raw value is not mandatory.
  8. //
  9. // {{- with $.Config.Annotations.GQL }}
  10. // {{/* Annotation usage goes here. */}}
  11. // {{- end }}
  12. //
  13. Annotations() []Annotation
  14. // Templates specifies a list of alternative templates
  15. // to execute or to override the default.
  16. Templates() []*gen.Template
  17. // Options specifies a list of entc.Options to evaluate on
  18. // the gen.Config before executing the code generation.
  19. Options() []Option
  20. }

To simplify the development of new extensions, developers can embed entc.DefaultExtension to create extensions without implementing all methods:

  1. package hello
  2. // GreetExtension implements entc.Extension.
  3. type GreetExtension struct {
  4. entc.DefaultExtension
  5. }

Adding Templates

Ent supports adding external templates that will be rendered during code generation. To bundle such external templates on an extension, implement the Templates method:

templates/greet.tmpl

  1. {{/* Tell Intellij/GoLand to enable the autocompletion based on the *gen.Graph type. */}}
  2. {{/* gotype: entgo.io/ent/entc/gen.Graph */}}
  3. {{ define "greet" }}
  4. {{/* Add the base header for the generated file */}}
  5. {{ $pkg := base $.Config.Package }}
  6. {{ template "header" $ }}
  7. {{/* Loop over all nodes and add the Greet method */}}
  8. {{ range $n := $.Nodes }}
  9. {{ $receiver := $n.Receiver }}
  10. func ({{ $receiver }} *{{ $n.Name }}) Greet() string {
  11. return "Hello, {{ $n.Name }}"
  12. }
  13. {{ end }}
  14. {{ end }}
  1. func (*GreetExtension) Templates() []*gen.Template {
  2. return []*gen.Template{
  3. gen.MustParse(gen.NewTemplate("greet").ParseFiles("templates/greet.tmpl")),
  4. }
  5. }

Adding Global Annotations

Annotations are a convenient way to supply users of our extension with an API to modify the behavior of code generation. To add annotations to our extension, implement the Annotations method. Let’s say in our GreetExtension we want to provide users with the ability to configure the greeting word in the generated code:

  1. // GreetingWord implements entc.Annotation.
  2. type GreetingWord string
  3. // Name of the annotation. Used by the codegen templates.
  4. func (GreetingWord) Name() string {
  5. return "GreetingWord"
  6. }

Then add it to the GreetExtension struct:

  1. type GreetExtension struct {
  2. entc.DefaultExtension
  3. word GreetingWord
  4. }

Next, implement the Annotations method:

  1. func (s *GreetExtension) Annotations() []entc.Annotation {
  2. return []entc.Annotation{
  3. s.word,
  4. }
  5. }

Now, from within your templates you can access the GreetingWord annotation:

  1. func ({{ $receiver }} *{{ $n.Name }}) Greet() string {
  2. return "{{ $.Annotations.GreetingWord }}, {{ $n.Name }}"
  3. }

Adding Hooks

The entc package provides an option to add a list of hooks (middlewares) to the code-generation phase. This option is ideal for adding custom validators for the schema, or for generating additional assets using the graph schema. To bundle code generation hooks with your extension, implement the Hooks method:

  1. func (s *GreetExtension) Hooks() []gen.Hook {
  2. return []gen.Hook{
  3. DisallowTypeName("Shalom"),
  4. }
  5. }
  6. // DisallowTypeName ensures there is no ent.Schema with the given name in the graph.
  7. func DisallowTypeName(name string) gen.Hook {
  8. return func(next gen.Generator) gen.Generator {
  9. return gen.GenerateFunc(func(g *gen.Graph) error {
  10. for _, node := range g.Nodes {
  11. if node.Name == name {
  12. return fmt.Errorf("entc: validation failed, type named %q not allowed", name)
  13. }
  14. }
  15. return next.Generate(g)
  16. })
  17. }
  18. }

Using an Extension in Code Generation

To use an extension in our code-generation configuration, use entc.Extensions, a helper method that returns an entc.Option that applies our chosen extensions:

ent/entc.go

  1. //+build ignore
  2. package main
  3. import (
  4. "fmt"
  5. "log"
  6. "entgo.io/ent/entc"
  7. "entgo.io/ent/entc/gen"
  8. )
  9. func main() {
  10. err := entc.Generate("./schema",
  11. &gen.Config{},
  12. entc.Extensions(&GreetExtension{
  13. word: GreetingWord("Shalom"),
  14. }),
  15. )
  16. if err != nil {
  17. log.Fatal("running ent codegen:", err)
  18. }
  19. }
  • elk (discontinued)
    elk is an extension that generates RESTful API endpoints from Ent schemas. The extension generates HTTP CRUD handlers from the Ent schema, as well as an OpenAPI JSON file. By using it, you can easily build a RESTful HTTP server for your application. Please note, that elk has been discontinued in favor of entoas. An implementation generator is in the works.
    Read this blog post on how to work with elk, and this blog post on how to generate an OpenAPI Specification.

  • entoas entoas is an extension that originates from elk and was ported into its own extension and is now the official generator for and opinionated OpenAPI Specification document. You can use this to rapidly develop and document a RESTful HTTP server. There will be a new extension released soon providing a generated implementation integrating for the document provided by entoas using ent.

  • entgql
    This extension helps users build GraphQL servers from Ent schemas. entgql integrates with gqlgen, a popular, schema-first Go library for building GraphQL servers. The extension includes the generation of type-safe GraphQL filters, which enable users to effortlessly map GraphQL queries to Ent queries.
    Follow this tutorial to get started.

  • entproto
    entproto generates Protobuf message definitions and gRPC service definitions from Ent schemas. The project also includes protoc-gen-entgrpc, a protoc (Protobuf compiler) plugin that is used to generate a working implementation of the gRPC service definition generated by Entproto. In this manner, we can easily create a gRPC server that can serve requests to our service without writing any code (aside from defining the Ent schema)!
    To learn how to use and set up entproto, read this tutorial. For more background you can read this blog post, or this blog post discussing more entproto features.

  • entviz
    entviz is an extension that generates visual diagrams from Ent schemas. These diagrams visualize the schema in a web browser, and stay updated as we continue coding. entviz can be configured in such a way that every time we regenerate the schema, the diagram is automatically updated, making it easy to view the changes being made.
    Learn how to integrate entviz in your project in this blog post.