Generating code
The howdy create commands write code into the project you are in, in the
shape howdy new gave it, with tests, and register it in the app. Four of
them add a database table with as much code around it as you ask for:
-
howdy create controllerwrites the table, a record, its database operations and a JSON API. -
howdy create pageswrites the same with HTML pages in place of the JSON API: a table of records, and forms to add, edit and delete them. -
howdy create repowrites the table, the record and its database operations, with no routes. -
howdy create migrationwrites only the table.
The rest add one thing at a time:
-
howdy create pageandhowdy create live_pageadd a page to a controller, creating the controller if needed. -
howdy create componentandhowdy create live_componentadd a reusable piece of view. -
howdy create layoutadds a layout for pages to render through. -
howdy create emailadds an email, once the project has mail.
Run them from inside a project made with howdy new. The
commands that write a table need a database, so they do not work in one
created with --no-database. Whether it is SQLite or PostgreSQL is detected
from gleam.toml, and the generated column types follow it. Every command
takes --dry-run to show what it would write, and --json for a result a
script can read; see The howdy command.
A controller
howdy create controller users user name:string age:int
The first argument is the plural. It names the folder, the table, the routes and the module of database operations. The second is the singular, which names the record module and its type. The rest are the fields, described below.
This creates the feature src/<project>/features/users/:
-
user.gleamholds theUserrecord, input validation, JSON codecs and the database table. -
users.gleamholds the database operations:all,find,create,updateanddelete. -
users_controller.gleamholds the routes.
The routes are:
-
GET /api/usersreturns every user. -
GET /api/users/:idreturns one user. -
POST /api/userscreates a user and answers 201. -
PUT /api/users/:idreplaces every field and answers 200. -
DELETE /api/users/:iddeletes a user and answers 204.
The command also writes:
-
migrations/create_users.gleam, a migration that creates theuserstable, numbered one above the highest migration in the project -
test/<project>/features/users/users_test.gleam, tests for the database operations -
test/<project>/features/users/users_controller_test.gleam, tests for the routes
Then it registers the controller in src/<project>.gleam and the migration
in platform/database.gleam, and runs gleam format. The migration runs the
next time the app starts, or when you run gleam run -m tasks/migrate.
Names of more than one word use underscores:
howdy create controller blog_posts blog_post title body:string published:bool
Pages
For pages that people use rather than a JSON API:
howdy create pages users user name:string age:int
howdy create pages takes the same arguments and writes the same record,
operations, migration and tests, with the routes under /users and a
pages/ folder in the feature:
-
pages/index_page.gleamshows every user, in a table. -
pages/show_page.gleamshows one user. -
pages/new_page.gleamis the form for a new user. -
pages/edit_page.gleamis the form to change one. -
pages/helpers.gleamholds the form both use, and where a user lives.
The routes are:
-
GET /usersshows every user, in a table. -
GET /users/newshows the form for a new user. -
POST /userscreates a user, then shows it. -
GET /users/:idshows one user. -
GET /users/:id/editshows the form to change it. -
POST /users/:idupdates the user, then shows it. -
POST /users/:id/deletedeletes the user, then goes back to the table.
The pages are built from Howdy UI and rendered on the
server as plain links and forms, so they work without JavaScript. That is
why update and delete are POST routes: an HTML form cannot send PUT or
DELETE. A form that fails validation comes back with the values still
filled in and an error under each field that needs fixing.
The pages render through the default layout in src/<project>/layouts/,
or the one --layout names. The first time you run it, the command also
writes the form helpers to platform/pages.gleam and adds the pages'
styles to priv/public/assets/style.css. Both happen once and serve every
set of pages, so they are yours to edit.
Pass --api to get the JSON controller from howdy create controller as
well, as users_api_controller.gleam, with the pages at /users and the
API at /api/users:
howdy create pages blog_posts blog_post title body published:bool --api
howdy create page users <name> adds another page to the same controller.
A repo
For data without any routes, reached from your own pages or from other features:
howdy create repo users user name:string email:string age:int
howdy create repo takes the same arguments and writes the same files,
except the controller and its test. It registers the migration only.
A migration
For only the table:
howdy create migration users name:string email:string age:int
This writes migrations/create_users.gleam, adds it to the list in
platform/database.gleam, and nothing else.
It takes the table name alone, without a singular, so every field needs its
type here. A bare name would be mistaken for the singular, and the command
tells you so.
A page
howdy create page blog about
howdy create page writes
src/<project>/features/blog/pages/about_page.gleam, a page ready for your
content, and adds its route to blog_controller.gleam beside the pages
folder: /blog/about, or /blog for a page named index. A test that the
page responds goes in test/<project>/features/blog/pages/.
If the controller does not exist it is created and registered in
src/<project>.gleam. If it does, the page is added to it and served under
whatever path the controller already uses. A page that already exists, or
a route the controller already has, is refused and nothing is written.
The page renders through the default layout, or the one --layout names:
howdy create page admin index --layout admin
A live page
howdy create live_page dashboard index
howdy create live_page writes the same page with a live section: a
Lustre server component whose model, update and
view run on the server, with the browser showing each change it sends over
a WebSocket. The controller gets two routes, the page at
/dashboard and its WebSocket at /dashboard/index/live. Everything else
is as for howdy create page, including --layout.
A component
howdy create component price_tag
howdy create component blog post_card
howdy create component writes a view function that returns an element,
for pages to reuse, with a test that it renders. With one name it is
global, in src/<project>/components/price_tag_component.gleam; with a
controller first it belongs to that controller, in
src/<project>/features/blog/components/. The controller must exist. Pages
call price_tag_component.view().
A live component
howdy create live_component notifications
howdy create live_component blog comments
howdy create live_component writes a Lustre server component, global or
belonging to a controller in the same way. Pages put it on the page with
notifications_component.mount() and render through their layout's
render_live, which loads the script it connects with.
Its WebSocket is routed for you. A component of a controller gets
/blog/components/comments/live in that controller. A global component
gets /components/notifications/live in
components/components_controller.gleam, which the first global live
component creates and registers in src/<project>.gleam. A test that it
mounts and serves its socket is written beside the other tests.
A layout
howdy create layout admin
howdy create layout writes src/<project>/layouts/admin_layout.gleam:
everything around a page's content, from the HTML document with its theme
and stylesheets to the header. It starts as a copy of the default layout
howdy new wrote, ready for you to change. Pages render through it with
render, or render_live when they have live sections. Pass
--layout admin to create page, create live_page or create pages to
use it.
An email
howdy create email password_reset
howdy create email writes src/<project>/emails/password_reset_email.gleam,
an email written with smail, with build(to:) to make the message and
preview() to build it from sample data. The preview is added to
emails/previews.gleam, so the dev admin shows it under Mail and the tests
check that it can be sent. The project needs mail first, from
howdy add mail.
Guarding with auth
Once the project has howdy add auth, --add-auth guards
what a command creates with Howdy Auth, so only someone signed in reaches
it, and anyone else gets 401:
howdy create controller notes note body --add-auth
howdy create pages notes note body --add-auth
howdy create page account index --add-auth
howdy create live_component notifications --add-auth
For create controller and create pages every route is guarded, and
with --api the JSON routes too. The controllers take the app's identity,
and their tests sign in. For create page, create live_page and a global
create live_component, the flag applies to the controller the command
creates. A controller that already exists keeps its guard, or lack of one,
and --add-auth is refused for one without.
Fields
Fields are name:type pairs. The types are string, int, float and
bool. For create controller, create pages and create repo a bare
name is a string, so title means title:string.
Every record also gets an integer id, so do not list one. Give at least
one field, and give each field once.
Names
The plural, the singular and the field names follow the same rule as project names: lowercase letters, digits and underscores, starting with a letter.
The plural and the singular must differ, because they name two modules in
one folder. Names that would not compile or would break the generated SQL
are refused with a message saying why: Gleam keywords, SQL keywords, names
of modules the generated code imports, and singulars that would shadow a
built-in Gleam type, such as list.
Your code is safe
Existing files are never overwritten. If any file the command would write already exists, it stops and writes nothing. The files shared by every set of pages are the exception: when they exist they are kept as they are, and the rest is written.
Registering a controller, pages, a component's socket or a migration means
editing src/<project>.gleam and platform/database.gleam. If either no
longer has the shape howdy new gave it, it is left alone, and the command
prints what to add by hand, as manual_steps with --json.
Flags
-
--api, forcreate pagesonly: also create the JSON controller under/api/<plural>. -
--layout <name>, forcreate page,create live_pageandcreate pages: render through this layout fromsrc/<project>/layouts/instead of the default. -
--add-auth: guard the routes with Howdy Auth; needshowdy add auth. -
--dry-run: show which files would be created and modified, without changing the project. -
--json: print the result as one JSON object: the files created and modified, steps left to do by hand, and what to do next. -
--plain: print progress as plain lines instead of animating.
