Posts tagged: dev

All posts with the tag "dev"

305 posts latest post 2026-07-05
Publishing rhythm
Jun 2026 | 3 posts
![[None]] jinja’s url_for in fastapi [1] does not account for https by default, there is probably a better way, but this is a way that allows me to configure when I use http vs https. @pass_context def https_url_for(context: dict, name: str, **path_params: Any) -> str: """ always convert http to https """ request = context["request"] http_url = request.url_for(name, **path_params) return str(http_url).replace("http", "https", 1) def get_templates(config: BaseSettings) -> Jinja2Templates: templates = Jinja2Templates(directory="templates") templates.env.globals["https_url_for"] = https_url_for ## only use the default url_for for local development, for dev, qa, and prod use https if os.environ.get("ENV") in ["dev", "qa", "prod"]: templates.env.globals["url_for"] = https_url_for console.print("Using HTTPS") else: console.print("Using HTTP") return templates References: [1]: /fastapi/
![[None]] import logging from typing import List import strawberry from fastapi import FastAPI from strawberry.fastapi import GraphQLRouter logger = logging.getLogger(__name__) authors = {} books = {} book_authors = {} authors_books = {} def get_author_for_book(root) -> "Author": return authors[book_authors[root.id]] @strawberry.type class Book: id: int title: str author: "Author" = strawberry.field(resolver=get_author_for_book) def get_books_for_author(root) -> List[Book]: print(f"getting books for {root}") return [books[i] for i in authors_books[root.id]] @strawberry.type class Author: id: int name: str books: List[Book] = strawberry.field(resolver=get_books_for_author) authors = {1: Author(id=1, name="Michael Crichton")} books = {1: Book(id=1, title="Jurassic Park")} # relationships book_authors[1] = 1 authors_books[1] = [1] def get_author_by_id(id: int) -> Author: return authors.get(id) def get_book_by_id(id: int) -> Book: return books.get(id) def get_authors(root) -> List[Author]: return authors.values() def get_books(root) -> List[Book]: print(books) print(authors) print(book_authors) print(authors_books) return books.values() @strawberry.typ...
605: Jim Nielsen on Subversive URLs, Blogging + AI, and Design Engineers Jim Nielsen joins us to about URLs and linking as the new subversive way to maintain the web, paying for news in Canada, should content creators be worried about AI, the case for design engineers, … ShopTalk · shoptalkshow.com [1] An absolute fantastic episode about blogging, thinking about a web1.0 kind of world today, and what it means moving forward. Web 1.0 is robust, you own your own destiny, you own your data, you can do what you want. There is no platform to tell you what you can and cannot do. But the future web is stealing your data to build AI models, spam sites are duplicating your content and stealing your SEO. You may or may not care, but at the end whether you get traffic or now you own your web 1.0 sites. References: [1]: https://shoptalkshow.com/605/

tailwind and markdown

This post is a bit of an experiment to see what I can do. Lets start with a block of pink text. I build my blog with my own static site generator called markata [1] Setup Tailwind for Jinja [2] Still Loving Tailwind [3] {.text-pink-500} This text should be pink This text should be not pink --- This text should be pink This text should be not pink --- Now will it work with bulleted lists {.text-pink-500} This block will be pink {.text-pink-500} * Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim * labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi * anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est This block will not be pink. * Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim * labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi * anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est --- - Lorem ipsum dol...
- Great take on low code. I have definitely felt the pressure of being presented low code options, “look it does almost everything you need, and you can do it without code.” Granted there are tons of great low code environments that serve their markets well (things like zapier). As pointed out here when they fall short rather than being hard, it goes to nearly impossible. As Theo points out here many applications follow an 80/20 rule. 80% of the app is really easy to put together, and takes about 20% of the time, probably less. What no code does is it takes that 80% that is already easy, makes it even easier ( pitches it as faster whether or not that is true ), and makes the last 20% of the project impossibly hard to create and maintain, so you just should have picked a tool that had the capability of doing the whole thing from the start anyways.
Template Designer Documentation — Jinja Documentation (3.1.x) jinja.palletsprojects.com [1] html [2] code generated by my jinja templates generally look half garbage because of indents and whitespace all over the place. I just learned about these pesky Whitespace Control characters that can get rid of the whitespace added from templating. You can also strip whitespace in templates by hand. If you add a minus sign (-) to the start or end of a block (e.g. a For tag), a comment, or a variable expression, the whitespaces before or after that block will be removed: {% for item in seq -%} {{ item }} {%- endfor %} References: [1]: https://jinja.palletsprojects.com/en/3.0.x/templates/#whitespace-control [2]: /html/
External Link stackoverflow.com [1] After struggling to get dependencies inside of middleware I learned that you can make global dependencies at the app level. I used this to set the user on every single route of the application without needing Depend on getting the user on each route. from fastapi import Depends, FastAPI, Request def get_db_session(): print("Calling 'get_db_session(...)'") return "Some Value" def get_current_user(session=Depends(get_db_session)): print("Calling 'get_current_user(...)'") return session def recalculate_resources(request: Request, current_user=Depends(get_current_user)): print("calling 'recalculate_resources(...)'") request.state.foo = current_user app = FastAPI(dependencies=[Depends(recalculate_resources)]) @app.get("/") async def root(request: Request): return {"foo_from_dependency": request.state.foo} References: [1]: https://stackoverflow.com/questions/72243379/fastapi-dependency-inside-middleware#answer-72480781
Handling Errors - FastAPI FastAPI framework, high performance, easy to learn, fast to code, ready for production fastapi.tiangolo.com [1] This page shows how to customize your fastapi [2] errors. I found this very useful to setup common templates so that I can return the same 404’s both programatically and by default, so it all looks the same to the end user. from fastapi import FastAPI, Request from fastapi.responses import JSONResponse class UnicornException(Exception): def __init__(self, name: str): self.name = name app = FastAPI() @app.exception_handler(UnicornException) async def unicorn_exception_handler(request: Request, exc: UnicornException): return JSONResponse( status_code=418, content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."}, ) @app.get("/unicorns/{name}") async def read_unicorn(name: str): if name == "yolo": raise UnicornException(name=name) return {"unicorn_name": name} References: [1]: https://fastapi.tiangolo.com/tutorial/handling-errors/ [2]: /fastapi/
logs with FastAPI and Uvicorn · Issue #1508 · fastapi/fastapi Hello, Thanks for FastAPI, easy to use in my Python projects ! However, I have an issue with logs. In my Python project, I use : app = FastAPI() uvicorn.run(app, host="0.0.0.0", port=8000) And when... GitHub · github.com [1] Setting an additional log handler to the uvicorn logger for access logs in fastapi [2] was not straightforward, but This post was very helpful. @app.on_event("startup") async def startup_event(): logger = logging.getLogger("uvicorn.access") handler = logging.StreamHandler() handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) logger.addHandler(handler) References: [1]: https://github.com/tiangolo/fastapi/issues/1508 [2]: /fastapi/
External Link stackoverflow.com [1] Setting tags in your fastapi endpoints will group them in the docs. You can also set some metadata around the tags to get nice descriptions. Here is a full example from the post. from fastapi import FastAPI tags_metadata = [ {"name": "Get Methods", "description": "One other way around"}, {"name": "Post Methods", "description": "Keep doing this"}, {"name": "Delete Methods", "description": "KILL 'EM ALL"}, {"name": "Put Methods", "description": "Boring"}, ] app = FastAPI(openapi_tags=tags_metadata) @app.delete("/items", tags=["Delete Methods"]) @app.put("/items", tags=["Put Methods"]) @app.post("/items", tags=["Post Methods"]) @app.get("/items", tags=["Get Methods"]) async def handle_items(): return References: [1]: https://stackoverflow.com/questions/63762387/how-to-group-fastapi-endpoints-in-swagger-ui#answer-63762765
Show some equivalent list comprehensions in filter examples · Issue #1068 · pallets/jinja I'm willing to write a pull-request for this, but I just want to see what people think before I write it. So the issue is this. I'm very familiar with python. I'm new to Jinja2. Often I find myself... GitHub · github.com [1] I often want to reach for non existing list comprehensions in jinja 2, Here are a few nice equivalents. a: {{ data | selectattr('x', 'gt', 5) | list }} b: {{ data | map(attribute='c') | list }} c: {{ data | selectattr('x', 'gt', 5) | map(attribute='c') | list }} References: [1]: https://github.com/pallets/jinja/issues/1068

I am working on fokais.com’s signup page, and I want to hide the form input during an htmx request. I was seeing some issues where I was able to prevent spamming the submit button, but was still able to get one extra hit on it.

It also felt like nothing was happening while sending the email to the user for verification. Now I get the form to disappear and a spinner to show during the request.

html">HTML #

Let’s start off with the form. It uses htmx to submit a post request to the post_request route. Note that there is a spinner in the post_request with the htmx-indicator class.

The intent is to hide the spinner until the request is running, and hide all of the form input during the request.

<form
  id="signup-form"
  hx-swap-oob="outerHTML"
  class="m-4 mx-auto mb-6 flex w-80 flex-col rounded-lg b p-4 shadow-xlc shadow-cyan-500/10"
  method="POST"
  action="{{ url_for('post_signup') }}"
  hx-post="{{ url_for('post_signup') }}"
>

<!--markata-attribution-->
  <input
    class="mx-8 mt-6 mb-4 border border-black bg-zinc-900 p-1 text-center focus:bg-zinc-800"

<!--markata-attribution-->
    type="text"

<!--markata-attribution-->
    value="{{ full_name }}"

<!--markata-attribution-->
    name="full_name"

<!--markata-attribution-->
    placeholder="Full Name"
  />

<!--markata-attribution-->
  {% if full_name_error %}

<!--markata-attribution-->
  <label class="-mt-6 mb-6 mx-8 text-red-500 p-1 text-center">

<!--markata-attribution-->
    {{ full_name_error }}

<!--markata-attribution-->
  </label>

<!--markata-attribution-->
  {% endif %}

<!--markata-attribution-->
  <input
    class="mx-8 mb-4 border border-black bg-zinc-900 p-1 text-center focus:bg-zinc-800"

<!--markata-attribution-->
    type="text"

<!--markata-attribution-->
    value="{{ username }}"

<!--markata-attribution-->
    name="username"

<!--markata-attribution-->
    placeholder="username"
  />

<!--markata-attribution-->
  {% if username_error %}

<!--markata-attribution-->
  <label class="-mt-6 mb-6 mx-8 text-red-500 p-1 text-center">

<!--markata-attribution-->
    {{ username_error }}

<!--markata-attribution-->
  </label>

<!--markata-attribution-->
  {% endif %}

<!--markata-attribution-->
  <input
    class="mx-8 mb-4 border border-black bg-zinc-900 p-1 text-center focus:bg-zinc-800"

<!--markata-attribution-->
    type="email"

<!--markata-attribution-->
    name="email"

<!--markata-attribution-->
    value="{{ email }}"

<!--markata-attribution-->
    placeholder="email"
  />

<!--markata-attribution-->
  {% if email_error %}

<!--markata-attribution-->
  <label class="-mt-6 mb-6 mx-8 text-red-500 p-1 text-center">

<!--markata-attribution-->
    {{ email_error }}

<!--markata-attribution-->
  </label>

<!--markata-attribution-->
  {% endif %}

<!--markata-attribution-->
  <input
    class="mx-auto w-32 mb-4 border border-black bg-purple-900 p-1 text-center focus:bg-zinc-800"

<!--markata-attribution-->
    type="submit"

<!--markata-attribution-->
    value="sign up"
  />

<!--markata-attribution-->
  <div role="status" class="mx-auto htmx-indicator">

<!--markata-attribution-->
    <svg

<!--markata-attribution-->
      class="mx-auto animate-spin h-5 w-5 text-white"

<!--markata-attribution-->
      xmlns="https://www.w3.org/2000/svg"

<!--markata-attribution-->
      fill="none"

<!--markata-attribution-->
      viewBox="0 0 24 24"
    >

<!--markata-attribution-->
      <circle

<!--markata-attribution-->
        class="opacity-25"

<!--markata-attribution-->
        cx="12"

<!--markata-attribution-->
        cy="12"

<!--markata-attribution-->
        r="10"

<!--markata-attribution-->
        stroke="currentColor"

<!--markata-attribution-->
        stroke-width="4"
      ></circle>

<!--markata-attribution-->
      <path

<!--markata-attribution-->
        class="opacity-75"

<!--markata-attribution-->
        fill="currentColor"
        d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
      ></path>

<!--markata-attribution-->
    </svg>

<!--markata-attribution-->
    <p>Signing up...</p>

<!--markata-attribution-->
  </div>

<!--markata-attribution-->
</form>

Yes this is styled using tailwindcss.

https://waylonwalker.com/still-loving-tailwind/

CSS #

Let’s take a look at how we achieve switching between only spinner an only form inputs using css.

.htmx-indicator {
  @apply hidden;
  opacity: 0;
  transition: opacity 500ms ease-in;
}
.htmx-request button,
.htmx-request input[type="submit"],
.htmx-request input,
.htmx-request label {
  @apply hidden;
}
.htmx-request .htmx-indicator {
  opacity: 1;
  @apply block;
}
.htmx-request.htmx-indicator {
  opacity: 1;
  @apply block;
}

Final Result #

Here is the final result of me signing up for a new account in fokais.

Adam Wathan (@adamwathan) on X Hear me out. X (formerly Twitter) · twitter.com [1] I’m going to give this trick a shot on my sites, and see how I like it. * { min-width: 0 } Down in the comments @adamwathan [2] goes on to say. Basically every layout overflow bug ever boils down to some flex or grid child needing min-width: 0 😄 Oh and @ryanflorence [3] also says in the comments. I … do this. References: [1]: https://twitter.com/adamwathan/status/1734696245015494711 [2]: https://twitter.com/adamwathan/ [3]: https://twitter.com/ryanflorence
Path Operation Advanced Configuration - FastAPI FastAPI framework, high performance, easy to learn, fast to code, ready for production fastapi.tiangolo.com [1] Excluding routes from fastapi docs, can be done from the route configuration using `include_in_schema`. This is handy for routes that are not really api based or duplicates. From the Docs # [2] from fastapi import FastAPI app = FastAPI() @app.get("/items/", include_in_schema=False) async def read_items(): return [{"item_id": "Foo"}] trailing slash # [3] I’ve had better luck just routing both naked and trailing slash routes in fastapi [4]. I’ve had api’s deployed as a subroute to a site rather than a subdomain, and the automatic redirect betweens them tended to always get messed up. This is pretty easy fix for the pain is causes just give vim a yyp, and if you don’t want deuplicates in your docs, ignore one. from fastapi import FastAPI app = FastAPI() @app.get("/items") @app.get("/items/", include_in_schema=False) async def read_items(): return [{"item_id": "Foo"}] favicon.ico # [5] Now you do not need to deploy favicons to your api in any way, it is nice to have it in your browser tab, but more importantly ...
Protect API docs behind authentication? · Issue #364 · fastapi/fastapi Basic Question Does FastAPI provide a method for implementing authentication middleware or similar on the docs themselves (e.g. to protect access to /docs and /redoc)? Additional context My company... GitHub · github.com [1] You can protect your fastapi [2] docs behind auth so that not only can certain roles not run certain routes, but they cannot even see the docs at all. This way no one that shouldn’t be poking around can even discover routes they shouldn’t be using. Here is the soluteion provided by @kennylajara [3] from fastapi import FastAPI from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html from fastapi.openapi.utils import get_openapi import secrets from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import HTTPBasic, HTTPBasicCredentials app = FastAPI( title="FastAPI", version="0.1.0", docs_url=None, redoc_url=None, openapi_url = None, ) security = HTTPBasic() def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): correct_username = secrets.compare_digest(credentials.username, "user") correct_password = secrets...
Cancel subscriptions Cancel subscriptions immediately or at the end of the subscription period with proration options, invoice handling, and automatic cancellation after failed payment attempts. stripe.com [1] This is a handy guide to cancelling stripe subscriptions. # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys import stripe stripe.api_key = "sk_test_51ODvHtB26msLKqCAPBAo1qkBBuIfT5tQBX6YFWCLMsPixIExxITCRVa9tNCIqkdQS8olhR79NYXsFWBPKsM3LbGO00zEcNQfNI" stripe.Subscription.modify( "sub_49ty4767H20z6a", cancel_at_period_end=True, ) You can even inverse it by flipping True to False and re activate the subscription. References: [1]: https://stripe.com/docs/billing/subscriptions/cancel#canceling
External Link stripe.com [1] You can find your customers next billing date through the stripe api by using Invoice. and passing in customer, customer_details, subscription, or schedule. import stripe stripe.api_key = "sk_test_51ODvHtB26msLKqCAPBAo1qkBBuIfT5tQBX6YFWCLMsPixIExxITCRVa9tNCIqkdQS8olhR79NYXsFWBPKsM3LbGO00zEcNQfNI" invoice = stripe.Invoice.upcoming(customer="cus_NeZwdNtLEOXuvB") Within the invoice, you can find the next_payment_attempt as a epoch. date = datetime.fromtimestamp(invoice.next_payment_attempt) amount = invoice.amount_due currency = invoice.currency References: [1]: https://stripe.com/docs/api/invoices/upcoming
Search Use the search APIs to look up and retrieve objects in your Stripe data. Using search is a faster alternative to paginating through all resources. stripe.com [1] Stripe has it’s own query language for querying data. I’m just getting into using it and it seems pretty good so far. I needed to lookup the price for products. I was able to find prices for my product using the python api as shown below. stripe.Price.search(query="active: 'true' and product: 'prod_P8SfwtxJ45cWE2'") References: [1]: https://stripe.com/docs/search#search-query-language