Custom services

The core services from Flamego are great, but they are certainly not enough for your web applications, Inevitably, you will start want to make your own middleware and custom services that are specifically fitting your needs.

Injecting services

The Flame instance is building on top of the inject.TypeMapper to provide service injections for your handlers. Both flamego.Flame and flamego.Context have embeded the inject.TypeMapper that allow you to inject services at anywhere you want.

The Map method is used to inject services (aka. map values to their own types), the injected service can be a concrete type (*log.Logger) or an interface (io.Writer):

  1. l := log.New(os.Stdout, "[Flamego] ", 0)
  2. f := flamego.New()
  3. f.Map(l)
  4. // or
  5. var w io.Writer = &bytes.Buffer{}
  6. f := flamego.New()
  7. f.Map(w)

The MapTo method works similar to Map but instead of mapping values to their own types, it allows you to map values to interfaces:

  1. buf := &bytes.Buffer{}
  2. f := flamego.New()
  3. f.MapTo(buf, (*io.Writer)(nil))

You may also use the MapTo method to transform interfaces to other interfaces:

  1. var w io.ReadCloser = io.NopCloser(&bytes.Buffer{})
  2. f := flamego.New()
  3. f.MapTo(w, (*io.Reader)(nil))
  4. f.MapTo(w, (*io.Closer)(nil))

::: warning The MapTo method does a naive mapping and runtime panic could occur if the interface you’re mapping to is not implemented by the type of the underlying value you’re giving. :::

Global services

When you inject services to the Flame instance without attaching to any route, these injected services are considered as global services, which are available for all handlers of the Flame instance.

There are two ways you can inject a global service, directly call Map or MapTo on the Flame instance, or through a global middleware using the Use method:

  1. db := database.New()
  2. f := flamego.New()
  3. f.Map(db)
  4. // or
  5. f := flamego.New()
  6. f.Use(func(c flamego.Context) {
  7. db := database.New()
  8. c.Map(db)
  9. })

Group services

When you inject services to a group of routes, these injected services are considered as group services, which are only available for all handlers within the group.

You can only inject a group service through a group middleware:

  1. f := flamego.New()
  2. f.Group("/user",
  3. func() {
  4. f.Get("/settings", func(user *database.User) {
  5. ...
  6. })
  7. },
  8. func(c flamego.Context) {
  9. user := database.GetUser()
  10. c.Map(user)
  11. },
  12. )
  13. f.Group("/repo", func() {
  14. f.Get("/settings", func(user *database.User) {
  15. // This handler will cause panic because *database.User is not available
  16. })
  17. })

In the above example, the *database.User is only available to the group of routes on line 3 to 7. Trying to use it outside the group will cause panic as illustrated on line 14.

Route-level services

When you inject services to a single route, these injected services are considered as route-level services, which are only available the handlers of that particular route.

You can only inject a route-level service through a route-level middleware:

  1. f := flamego.New()
  2. f.Get("/user",
  3. func(c flamego.Context) {
  4. user := database.GetUser()
  5. c.Map(user)
  6. },
  7. f.Get("/settings", func(user *database.User) {
  8. ...
  9. }),
  10. )
  11. f.Get("/repo", func(user *database.User) {
  12. // This handler will cause panic because *database.User is not available
  13. })

In the above example, the *database.User is only available to the route on line 7 to 9. Trying to use it in all other routes will cause panic as illustrated on line 11.

Overriding services

Injected services can be overridden when you’re not happy with the service functionality or behaviors provided by the other middleware.

Here is an example of overriding a global service at the route level:

  1. package main
  2. import (
  3. "bytes"
  4. "io"
  5. "github.com/flamego/flamego"
  6. )
  7. func main() {
  8. f := flamego.New()
  9. f.Use(func(c flamego.Context) {
  10. buf := bytes.NewBufferString("this is from a global service")
  11. f.MapTo(buf, (*io.Reader)(nil))
  12. })
  13. f.Get("/",
  14. func(c flamego.Context) {
  15. buf := bytes.NewBufferString("this is from a route-level service")
  16. f.MapTo(buf, (*io.Reader)(nil))
  17. },
  18. func(r io.Reader) string {
  19. p, err := io.ReadAll(r)
  20. if err != nil {
  21. // Handler error
  22. }
  23. return string(p)
  24. },
  25. )
  26. f.Run()
  27. }

When you run the above program and do curl http://localhost:2830/, the following content are printed to your terminal:

  1. $ curl http://localhost:2830
  2. this is from a route-level service