Scripts provide a way to run code inside the framework context, but outside of the usual web request/response lifecycle. They’re perfect for cron jobs, one-off tasks, and administrative operations.
Common Use Cases
- Sending periodic email reminders
- Generating invoices
- Data migrations and imports
- Running as cronjobs
- Background job-queue processing
- Administrative tasks
Creating a New Script
Generate the script
Use the built-in generator to create a new script:new-script HelloWorldToAllUsers
This creates a file at Application/Script/HelloWorldToAllUsers.hs:#!/usr/bin/env run-script
module Application.Script.HelloWorldToAllUsers where
import Application.Script.Prelude
run :: Script
run = do
-- Your script logic here
Implement your logic
The run function is your entry point. You can use all framework functions - database queries, email sending, view rendering, etc.#!/usr/bin/env run-script
module Application.Script.HelloWorldToAllUsers where
import Application.Script.Prelude
run :: Script
run = do
users <- query @User |> fetch
forEach users \user -> do
putStrLn $ "Hello World, " <> user.firstname <> "!"
This fetches all users and prints a greeting for each one.
Running Scripts
Local Development
When running scripts locally, ensure the IHP server is running in the background with devenv up. Scripts use the same database connection and resources as your application.
Scripts are executable by default thanks to the shebang line #!/usr/bin/env run-script:
./Application/Script/HelloWorldToAllUsers.hs
Output:
Hello World, Alice!
Hello World, Bob!
Hello World, Charlie!
If you get a permission error, add the executable flag: chmod +x Application/Script/HelloWorldToAllUsers.hs
Running from GHCI
You can test scripts interactively in the REPL:
Then load and run your script:
:l Application.Script.TestScript
runScript ihpDefaultConfig run
The ihpDefaultConfig is available from Application.Script.Prelude and provides default configuration. You can substitute it with custom configuration from Config.hs.
Custom Configuration
Define custom configurations for different environments:
-- Config.hs
import qualified IHP.Log as Log
import IHP.Log.Types
testConfig :: ConfigBuilder
testConfig = do
option Development
logger <- liftIO $ newLogger def {
level = Debug,
formatter = withTimeAndLevelFormatter,
destination = File "Log/App.log" (SizeRotate (Bytes (4 * 1024 * 1024)) 4) defaultBufSize
}
option logger
Use it in your script:
#!/usr/bin/env run-script
module Application.Script.TestScript where
import Application.Script.Prelude
import Config
run :: (?modelContext :: ModelContext, ?context :: FrameworkConfig) => IO ()
run = do
user <- query @User
|> filterWhere (#name, "Php")
|> fetchOne
user
|> set #name "IHP"
|> updateRecord
pure ()
Run with custom config:
IHP> :l Application.Script.TestScript
IHP> runScript testConfig run
Production Deployment
Building Scripts
Build scripts to binaries for production use:
# Build all scripts along with the application
nix build .#optimized-prod-server
# Or for faster, unoptimized builds
nix build .#unoptimized-prod-server
This produces binaries in result/bin/. A script at Application/Script/HelloWorldToAllUsers.hs becomes result/bin/HelloWorldToAllUsers.
Run the compiled script:
result/bin/HelloWorldToAllUsers
Scheduling with Cron
Add scripts to your crontab for periodic execution:
# Run daily at 2 AM
0 2 * * * /path/to/result/bin/SendDailyDigest
# Run every hour
0 * * * * /path/to/result/bin/ProcessQueue
Best Practices
- Keep scripts idempotent - Scripts should be safe to run multiple times
- Add logging - Use
IHP.Log to track script execution
- Handle errors gracefully - Catch exceptions and log failures
- Test in development - Use GHCI to test scripts before deployment
- Monitor execution - Track script runs in production logs
See Also
- Jobs - For recurring background tasks
- Logging - Add logging to your scripts
- Configuration - Custom framework configuration