# Services and providers

> Move the blog App's post store into the container with a service provider created by act make:provider, and resolve it where the routes register.

This chapter turns the post store into a service the whole App shares. You will learn how to:

- Create a service provider with `act make:provider`
- Bind the store in the App's container as a singleton
- Resolve the store when the routes register
- Tell what happens when a binding is missing

## Overview

`Routes` creates the store today, so only the handlers can reach it. The next chapter adds a Command that lists the posts, and the chapter after it replaces the store in tests: both need the store without going through the routes.

Every App has a **container**, from the `di` package, that knows how to build each of the App's services and how long each one lives. A **service provider** is where a service joins the App: its `Register` hook binds the service in the container, and its other hooks, all optional, run at the App's boot, start, ready and shutdown. Code anywhere in the App then asks the container for the service by its type, and gets the same one.

## Create the provider

`act make:provider` writes a provider into `app/providers` and adds it to the App:

```sh
act make:provider posts
```

```text title="Output"
CREATE app/providers/posts_provider.go
UPDATE start/app.go
         + app.Provide(&providers.PostsProvider{})
```

The generator added two lines to `start/app.go`: the import, and `app.Provide`, which hands the provider to the App before it boots:

```go title="start/app.go" ins={14,28}
// Package start assembles the App: its configuration, services,
// middleware and routes. main runs the App it builds, and tests build it
// the same way.
package start

import (
	"context"

	"github.com/mucolabs/tinka"
	"github.com/mucolabs/tinka/encryption"
	"github.com/mucolabs/tinka/log"

	"blog/app/exceptions"
	"blog/app/providers"
	"blog/config"
)

// App builds the App from its configuration.
func App(cfg config.Config) (*tinka.App, error) {
	enc, err := encryption.New(cfg.App.Key)
	if err != nil {
		return nil, err
	}
	cfg.HTTP.Logger = log.New(cfg.Logger)
	cfg.HTTP.Encrypter = enc

	app := tinka.New(cfg.HTTP)
	app.Provide(&providers.PostsProvider{})
	app.OnError(exceptions.Handler)
	Kernel(app)
	app.OnStart(func(context.Context) error { return Routes(app) })
	return app, nil
}
```

## Bind the store

The generated provider has three empty hooks: `Register`, `Boot` and `Shutdown`. The store needs only `Register`, since memory needs no check at boot and nothing to release at shutdown.

`Register` binds `*posts.Store` with `Singleton`: the factory you pass runs the first time something asks the container for a `*posts.Store`, and every later request gets that same store. The factory's return type is the binding's key, so the compiler checks the wiring, and there's no name to misspell. Replace the file's contents:

```go title="app/providers/posts_provider.go"
package providers

import (
	"github.com/mucolabs/tinka/di"

	"blog/app/posts"
)

// PostsProvider adds the posts store to the App.
type PostsProvider struct{}

// Register binds the store as a singleton: the App builds one, the first
// time something asks for it, and every handler and Command shares it.
func (p *PostsProvider) Register(c *di.Container) {
	c.Singleton(func(*di.Resolver) (*posts.Store, error) {
		return posts.NewStore(), nil
	})
}
```

When a database replaces the store, this provider is where it grows: `Register` binds the connection pool, and a `Boot` hook checks the database answers, so a misconfigured App stops at boot instead of failing its first request.

## Resolve the store in the routes

`Routes` runs while the App starts, after every provider registered its bindings, so it can ask the container for the store. `app.Container()` returns the App's container, and `Make[*posts.Store]()` returns the store, building it on this first call, or an error when nothing provides it. `Routes` returns that error, which stops the App:

```go title="start/routes.go" del={1} ins={2-6}
	postHandlers := handlers.Posts{Store: posts.NewStore()}
	store, err := app.Container().Make[*posts.Store]()
	if err != nil {
		return err
	}
	postHandlers := handlers.Posts{Store: store}
	app.Get("/posts", postHandlers.List)
```

The handlers resolve nothing per request: they hold the store they were given, so a request pays nothing for the container.

:::tip
A store nothing provides stops the App as it starts, before it answers a single request. If you forget `app.Provide`, the App logs why and exits with status `1`:

```text title="Output"
[01:48:14.099] ERROR: the App stopped err="tinka: start hook: di: cannot make *posts.Store: *posts.Store is not registered"
```

Add the provider back in `start/app.go` to fix it.
:::

## Check the App

Save the files. The App behaves as before, now with its store from the container. Create a post, then list them:

```sh
curl -X POST localhost:3000/posts -d 'title=Services+and+providers&body=The+store+moves+into+the+container.'
curl localhost:3000/posts
```

```text title="Output"
{"id":3,"title":"Services and providers","body":"The store moves into the container."}
[{"id":1,"title":"Hello, Tinka","body":"The first post on the blog."},{"id":2,"title":"Routes and handlers","body":"How a request finds its handler."},{"id":3,"title":"Services and providers","body":"The store moves into the container."}]
```

`act about` describes the App, and now lists the provider:

```sh
act about
```

```text title="Output"
Environment
  Mode       development
  Debug      on
  Address    :3000
  Providers  *providers.PostsProvider
```

The output above leaves out the first lines, which name the versions of Tinka and Go and your platform.

## Recap

`PostsProvider` binds the store in the App's container as a singleton, and `start/app.go` adds the provider with `app.Provide`. `Routes` resolves the store with `Make` when the App starts, and a missing provider stops the App with an error before it serves anything. Next, [Commands](commands.md) uses the same store from the terminal.

See also: [Service providers](../guides/concepts/service-providers.md), [Dependency injection](../guides/concepts/dependency-injection.md), [Application lifecycle](../guides/concepts/application-lifecycle.md), [Scaffolding](../guides/concepts/scaffolding.md)
