App lifecycle
This page follows an app from prea.Run to the moment it stops. Knowing the order helps you understand when your code runs, and why most mistakes stop the app before it serves.
What you'll learn
- Everything
prea.Rundoes at startup, in order - When the components'
InitandCloserun - How the app shuts down without dropping requests
Startup
prea.Run goes through these steps. If any of them fails, it logs the error and the process exits with code 1.
- Load the configuration. It reads
.env, thenconfig/application.yamland the active profile's file.server.portandserver.modemust be set. - Set up the logs. It applies
logging.levelandlogging.format, orpreas.Loggingwhen set. - Connect to the database, if
database.urlis set. It checks the connection right away, so a wrong address fails now and not on the first request. - Run a migration command and exit, when started by
prea migrate. Migrations explains this. - Check the token secret, if
security.jwt.secretis set. A secret shorter than 32 bytes stops the app. The app still builds its ownsecurity.JwtwithNewJwtwhen it needs to sign tokens. - Create the components. It loads every preá in
Modules, creates their components and fills theirinject:""andvalue:""fields. A missing dependency stops the app here. - Prepare the database. It generates and applies migrations and checks the tables against the models, depending on the database settings.
- Start the components. It calls each
Init, dependencies first. - Mount the routes of every handler, with the security protections and the middlewares.
- Serve on
server.port.
level=INFO msg="prea: config loaded" profile=dev
level=INFO msg="database: applied 0.0.2_add_due_date (4ms)"
level=INFO msg="prea: listening" addr=[::]:8000While serving
Each request runs in its own goroutine. The components are shared by all of them, which is why a component never keeps request data in its fields.
The server settings protect the app from slow or stuck clients: how long a client may take to send the headers, the whole request, or to read the reply.
Shutdown
When the process gets SIGINT (Ctrl+C) or SIGTERM (what deploy platforms send):
- The server stops accepting new connections.
- It waits for the requests in flight, up to
server.shutdown_timeout(15 seconds by default). - It calls each component's
Close, in the reverse order ofInit. - It closes the database, after the components, so no query loses its connection halfway.
level=INFO msg="prea: shutting down"
level=SUCCESS msg="prea: stopped"If the requests are still running when the timeout ends, their connections close and their context is cancelled. That is one more reason to pass c.Request.Context() to every query. The app then exits with an error:
level=ERROR msg="prea: stopped" error="prea: shutdown timed out: the requests still running were cancelled (server.shutdown_timeout)"A second Ctrl+C stops the process at once, without waiting.
Next steps
- Models & schema: the database, starting from your structs
- Deploying: the lifecycle in production