Apps¶
Snok is great for scaffolding out RESTful APIs with FastAPI.
Getting ready¶
First set up your local environment by creating a directory and activating a virtual environment:
Assuming you have python 3.11+ installed:
cd $HOME/Desktop
mkdir -p myapp && cd myapp
python -m venv .venv
source .venv/bin/activate
You should see the path to your virtual environment now:
$ which python pip
/Users/anthony/Desktop/myapp/.venv/bin/python
/Users/anthony/Desktop/myapp/.venv/bin/pip
Installing Snok¶
Install Snok with pip:
pip install snok
Check that snok was installed:
snok
If you're using pyenv (recommended), you may have to rehash your shims
pyenv rehash
Creating a new app¶
Create a new app with:
snok new myapp --type app
Let's build some stuff!¶
You can generate the following kinds of scaffolding;
- Database Models
- FastAPI APIRouters
- Full CRUD Scaffolding
Let's walk through an example of each.
Prerequisites¶
First you'll have to run a database. Snok supports Postgres at the moment and will support other SQL databases in the future.
You will need to have docker installed and running on your machine.
Run the following to make sure docker is running:
docker ps
If that doesn't work you can learn how to install docker here.
Once you're all set up you can run a postgres database with:
docker run --rm --name=postgres -d -p 5432:5432 -e POSTGRES_USER=myapp -e POSTGRES_PASSWORD=myapp -e POSTGRES_HOST_AUTH_METHOD=password -e POSTGRES_DB=myapp postgres:15
Check that everything's ready to go!¶
snok ok
Run the server!¶
In one terminal session run:
snok server
And in another terminal session run:
curl -s -X GET http://127.0.0.1:8000/livez
You should see something to the effect of:
{"message":"ok"}
Let's stop the server and commit our code to the git repo snok set up for us before moving on to the next step.
Create a Database Model¶
Let's create a database model for a blog post.
snok generate model post title:str body:str
Snok created a new file in myapp/models/post.py
that looks like this:
from myapp.kit.db import RecordModel
class Posts(RecordModel, table=True):
__tablename__ = "posts"
title: str
body: str
It also added the import statement to myapp/models/__init__.py
.
Let's run the migrations to create the table in our database.
Make sure the development db is set up
PGPASSWORD=myapp psql -h 0.0.0.0 -U myapp -c "create database myapp_development owner myapp;"
And run the migrations:
alembic revision --autogenerate -m "posts"
alembic upgrade head
Now let's check that the table was created:
$ PGPASSWORD=myapp psql -h 0.0.0.0 -U myapp -d myapp_development -c "\d posts"
Table "public.posts"
Column | Type | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+---------
created_at | timestamp without time zone | | not null |
updated_at | timestamp without time zone | | not null |
deleted_at | timestamp without time zone | | |
id | uuid | | not null |
title | character varying | | not null |
body | character varying | | not null |
Indexes:
"pk_posts" PRIMARY KEY, btree (id)
"ix_posts_id" btree (id)
Wow! That's cool! Let's commit our code to git again and move on to the next part.
If you run into any pre-commit snags, you can run snok format
to format your code. Snok uses ruff
under the hood to format your code, so you can run ruff
directly if you want to as well.
Create FastAPI APIRouters¶
Creating routers is fairly straightforward.
snok g router myrouter hello world
Now run the server again and check out the new endpoint:
snok server
If you go to http://127.0.0.1:8000/docs in your browser you should see the new endpoints!
Fill those in however you like.
Commit your code again and let's move on to our last example for now.
Create Full CRUD Scaffolding¶
Now for the fun part!
Let's create a full CRUD scaffold for an "authors" entity.
snok g scaffold author name:str age:int
Wow! This created a bunch of files
$ git status -sb -u
## main
M myapp/models/__init__.py
M myapp/router.py
?? myapp/authors/__init__.py
?? myapp/authors/router.py
?? myapp/authors/schemas.py
?? myapp/authors/service.py
?? myapp/models/authors.py
?? tests/authors/__init__.py
?? tests/authors/test_router.py
This generated tests for the router and service, as well as the router, service, and model files themselves.
Let's test this code out!
snok test
Awesome! We just generated a fully tested CRUD feature with tests super fast!
Let's run our migrations.
alembic revision --autogenerate -m "authors"
alembic upgrade head
Let's check the table
$ PGPASSWORD=myapp psql -h 0.0.0.0 -U myapp -d myapp_development -c "\d authors"
Table "public.authors"
Column | Type | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+---------
created_at | timestamp without time zone | | not null |
updated_at | timestamp without time zone | | not null |
deleted_at | timestamp without time zone | | |
id | uuid | | not null |
name | character varying | | not null |
age | integer | | not null |
Indexes:
"pk_authors" PRIMARY KEY, btree (id)
"ix_authors_id" btree (id)
Let's run the server again and check out the new endpoints:
snok server
List authors
curl -X 'GET' \
'http://127.0.0.1:8000/api/v0/authors?order_by=updated_at&order_direction=desc&offset=0&limit=10' \
-H 'accept: application/json'
Create an author
curl -X 'POST' \
'http://127.0.0.1:8000/api/v0/authors' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "anthony",
"age": 42
}'
Grab the ID of the author you just created and update it
ID=$(curl -s -X 'GET' 'http://127.0.0.1:8000/api/v0/authors?order_by=updated_at&order_direction=desc&offset=0&limit=10' -H 'accept: application/json' | jq -r '.[0] | .id')
curl -X 'PUT' \
http://127.0.0.1:8000/api/v0/authors/${ID} \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "anthony",
"age": 24
}'
Now delete the user you just created 😭
curl -X 'DELETE' \
http://127.0.0.1:8000/api/v0/authors/${ID} \
-H 'accept: */*'
You shouldn't be able to find the user anymore
curl -X 'GET' \
'http://127.0.0.1:8000/api/v0/authors?order_by=updated_at&order_direction=desc&offset=0&limit=10' \
-H 'accept: application/json'
Super cool! Let's commit our code again and revel in our glory! We just made a full CRUD feature in a few minutes!