AI 资讯
Your Scraper Works Locally but Returns 403 on a Server. Here's Why.
Key takeaways A request is judged on many layers at once — IP reputation, TLS fingerprint, HTTP/2 shape, headers, and how the browser is driven — and failing any one is enough for a 403. Your laptop passes because every layer is consistent with a real home browser; a server changes one (usually the IP) and the inconsistency is the tell. A 403 with no challenge page almost always means you were blocked at the network layer (IP/ASN reputation or TLS/JA3 fingerprint) before any HTML was served — not a credentials or rate-limit bug, so 'add a User-Agent' or 'slow down' won't fix it. The fix order that actually works: get off datacenter IPs (residential/ISP proxies), match a real browser's TLS fingerprint, and spin up a real-browser stealth setup only for the pages that truly need JavaScript — escalate, don't lead with a browser. A proxy only changes your IP; a Linux VPS still leaks a Linux-shaped TLS/JS fingerprint, so 'residential IP + datacenter everything-else' is a contradiction a real home machine never makes — which is why a proxied server can get blocked harder than your laptop. Your scraper runs perfectly on your laptop. You deploy it to a VPS or a CI runner, change nothing in the code, and suddenly every request comes back 403 . It feels like a bug — the code is identical — but it usually isn't. Anti-bot systems judge a request on many signals at once, and moving from your home machine to a datacenter flips several of them at the same time. This post breaks down exactly which signals change, how to tell which one is blocking you, and how to fix it — for authorized access to public data (we'll keep that framing honest throughout; nothing here is about defeating a protection). A request is judged in two stages It helps to know that detection happens in two stages: Stage 1 — before any HTML is served. IP reputation, your TLS handshake, your HTTP/2 settings, and header order are all inspected on the connection itself, passively and cheaply, before your request is e
AI 资讯
Build a Typed Training Data Client in TypeScript with intervals-icu
If your training dashboard starts as one HTTP request and grows into athletes, activities, wellness, workouts, gear, and performance data, a hand-written fetch wrapper becomes expensive to maintain. Every new endpoint adds another URL, another response shape, and another place to get authentication or retry behavior wrong. This tutorial shows a small, reproducible path with intervals-icu , an open-source TypeScript client for the Intervals.icu API . The goal is not to build a complete training application. It is to establish a typed client, choose the right authentication boundary, call one service, and understand what changes when you move from version 1 to version 2 of the library. TL;DR Install the stable npm package, create an IntervalsClient with an API key or OAuth access token, and use service accessors such as client.athletes or client.activities. Version 2 uses typed service methods, retries selected transient failures, and defaults requests to the authenticated athlete. Prerequisites You need: Node.js 18 or newer. npm. An Intervals.icu account with an API key, or an OAuth access token for an application acting for other users. A TypeScript project that can run ESM modules. The published package is intervals-icu version 2.2.1, and its package metadata declares Node.js >=18.0.0. The repository is public and licensed under MIT. The examples below target that stable package version, not an unreleased default-branch change. Install the stable client Create a small project and pin the package version used in this tutorial: mkdir intervals-demo cd intervals-demo npm init -y npm install intervals-icu@2.2.1 npm install -D typescript tsx The package publishes both ESM and CommonJS entry points and exposes TypeScript declarations from its package root. Add a script so a .ts file can run without a separate build step: { "type" : "module" , "scripts" : { "start" : "tsx src/index.ts" } } Create the smallest useful client Create src/index.ts. Keep the credential outside
AI 资讯
# What I Learned from Building with GIS Data and the Copernicus API at the KijaniSpace Hackathon
As software developers, we often spend most of our time building APIs, databases, authentication systems, and web applications. That's certainly been my focus recently, especially working with Go, JWT authentication, and backend services. Last week, however, I had the opportunity to participate in the KijaniSpace Hackathon , held at Zone01 Kisumu , and it introduced me to an entirely different side of software development. Our challenge was to build solutions using: Geographic Information Systems (GIS) The Copernicus API IoT devices where applicable It was an opportunity to see how software can interact with our physical world. What is GIS? GIS (Geographic Information Systems) is a technology used to collect, analyze, visualize, and manage data that has a geographic location. Imagine not just storing information like: Temperature Population Vegetation Buildings Roads ...but also knowing exactly where that information exists on Earth. That location data allows developers to build intelligent systems capable of answering questions like: Which farms are experiencing drought? Which roads are likely to flood? Which areas are losing forest cover? Where should new infrastructure be built? GIS transforms ordinary data into meaningful geographic insights. Discovering the Copernicus Program Before this hackathon, I had heard very little about Copernicus. Copernicus is the European Union's Earth Observation Programme. It provides free satellite imagery and environmental data collected by the Sentinel satellite missions. Through its APIs, developers can access information about: Land cover Vegetation health Weather patterns Water bodies Air quality Climate changes Disaster monitoring What amazed me most is that much of this data is openly available for developers to build impactful applications. Where IoT Fits In Some teams also explored Internet of Things (IoT) solutions. IoT devices can collect real-world information through sensors measuring: Soil moisture Temperature Humidi
AI 资讯
Building a Modern Rate Limiter and DDoS Protection Library for Python
Rate limiting is one of those features every production API eventually needs. Whether you're building a public REST API, a WebSocket service, or an authentication endpoint, you'll eventually face problems like: Credential stuffing Brute-force attacks API abuse Bots scraping your endpoints Unexpected traffic spikes Most applications solve this with a simple request counter. But after building several APIs with Django, FastAPI, and Flask, I realized that production traffic requires much more than "X requests per minute." That observation led me to build drogue , an open-source Python library for rate limiting and traffic protection. The Problem Traditional rate limiting is straightforward: Allow 100 requests per minute. This works well for many cases, but real-world applications quickly expose its limitations. For example: A distributed attack can remain below the per-IP limit. A bot can rotate through proxies. WebSocket connections often require different handling than HTTP requests. Different endpoints need different protection strategies. I wanted a system that could go beyond simple request counting. Design Goals From the beginning, I focused on a few principles. 1. Clean framework integration I didn't want endpoint functions filled with framework-specific plumbing. Instead, the library should feel like a natural extension of the framework. from fastapi import FastAPI from drogue.adapters.fastapi import DrogueLimiter app = FastAPI () limiter = DrogueLimiter ( app , default_limits = [ " 100/minute " ]) @app.get ( " /users " ) @limiter.limit ( " 10/minute " ) async def users (): return { " status " : " ok " } No additional request objects. No complicated middleware configuration. Minimal boilerplate. Multiple Rate Limiting Algorithms Different applications require different algorithms. Instead of supporting only one approach, drogue includes multiple options: Token Bucket Sliding Window Fixed Window Each has different trade-offs between accuracy, burst handling, and
AI 资讯
How OAuth Works — hand out a token, never the password
"Log in with Google" — without Google ever seeing the other site's password. OAuth lets one app act on your behalf at another service without ever handling your password. Instead of credentials, apps get a scoped, revocable token. The authorization-code flow Redirect. The app sends you to the provider with the scopes it wants. Consent. You authenticate with the provider and approve (or deny) those scopes. Code. The provider redirects back to the app with a short-lived authorization code. Token exchange. The app's server swaps the code (plus its secret) for an access token. Use & refresh. The app calls APIs with the token, refreshing it as needed. Why it's safer than sharing a password Scoped. A token grants only the permissions you approved, not full account access. Revocable. You can revoke one app without changing your password. PKCE. Public clients add a proof step so an intercepted code alone is useless. The one-line mental model Hand out a narrow, revocable token — never the password itself. This is part of LearningTechBasics — one tech idea a day, each with an animated diagram and a 60-second narrated video. 📊 Animated version with the live diagram Follow @amtocbot · #LearningTechBasics
AI 资讯
Decoupled User Management in Python
Hands-On tests with 'UserHarbor' and IBM Bob: A Modular Approach to Python User Authentication and Permissions Introduction When evaluating open-source libraries for core application infrastructure - such as authentication, session management, and fine-grained Role-Based Access Control (RBAC) - getting hands-on with a complete reference application is invaluable. This is especially true for libraries aiming to be framework-agnostic, promising flexibility but requiring more explicit wiring. Recently, I wanted to explore UserHarbor ( github.com/userharbor/userharbor ), a lightweight Python user-management library designed without direct coupling to any web framework or database toolkit. Rather than manually bootstrapping a new project, setting up the SQLite database, and writing boilerplate code to explore every edge of the library, I used IBM Bob , to scaffold and implement an end-to-end reference demonstration integrated with FastAPI , SQLAlchemy for persistence, and SMTP (or a local console fallback) for transactional emails. The goal was to rapidly test UserHarbor's entire feature lifecycle - registration, email verification, session tokens, optional authentication, RBAC guards, password resets, and account deletion, which I personally find really useful. These capacities could be implemented in many applications and ease the phase of user registration, email validation etc… This post details the architecture built, highlights the key implementation logic, and illustrates how easily a decoupled core can be integrated into a modern web stack. UserHabor (from official GitHub repostory) Image from official project's repository Project status: UserHarbor is currently in an early stage of development. The API may change frequently. The library is not ready for production use yet . UserHarbor is a framework-agnostic Python library for user account management. Its goal is to provide a simple, stable, and framework-independent interface for common user-related operations:
AI 资讯
Scraping platform costs: measure successful rows, not browser minutes
A scraping job usually fails in boring ways: the browser hangs, a selector starts returning empty strings, a login expires, or the target site returns a captcha halfway through the run. The awkward part is that many platforms still bill you for the work done before the failure. If you run enough jobs, that difference shows up both in your invoice and in the amount of defensive code you need around the scraper. Billing by compute time changes how you build A lot of scraping platforms charge for runtime. Apify, for example, uses compute units: memory multiplied by time. A browser-heavy actor running for ten minutes with 2 GB of RAM consumes roughly a third of a compute unit before any actor-specific result fees. That model is reasonable from the provider side. Chromium processes are expensive. Proxies cost money. Retries use resources. But as the caller, you care about a different unit: did I get the rows I needed? The hard part is that runtime billing makes cost hard to know before execution. A job that normally takes 30 seconds might take 8 minutes when a site slows down. A job that returns malformed data can still count as successful from the platform's point of view. A job that fails after rendering 200 pages still consumed browser time. If your pipeline runs once a day, that may be fine. If it runs continuously, you probably want a local cost model that tracks outcomes, not just requests. type ScrapeRun = { jobId : string ; target : string ; startedAt : string ; finishedAt ?: string ; status : " queued " | " running " | " succeeded " | " failed " ; rowsExpected ?: number ; rowsReceived ?: number ; billedUnits ?: number ; }; function isUsefulResult ( run : ScrapeRun ) { if ( run . status !== " succeeded " ) return false ; if ( run . rowsExpected && ( run . rowsReceived ?? 0 ) < run . rowsExpected * 0.9 ) { return false ; } return ( run . rowsReceived ?? 0 ) > 0 ; } function costPerUsefulRow ( run : ScrapeRun ) { if ( ! isUsefulResult ( run )) return Infinity ; ret
AI 资讯
Picking a text-to-image API for a SaaS app: REST, pricing, and safety
If you just want the recommendation: call a plain REST image generation endpoint from your Node.js backend, keep the prompt-in / image-out path as dumb as you can stand, and add a chat model on top only when you actually need policy checks or structured prompts. For a first text-to-image feature inside a SaaS app, that is the entire architecture worth building. I've shipped that feature twice. Both times the generation call was the boring part. What ate the calendar was everything around it: deciding whether the output was safe to show a paying customer, reading the licence terms closely enough to know we could put generated art in a customer's exported PDF, storing the result somewhere that wasn't the provider's temporary URL, and — the part I got wrong, which I'll come back to — making retries safe. I run a one-person company, so I optimise for the number of moving parts I have to keep in my head at 2am, and a text-to-image feature that pulls in three new vendors is a feature I'll quietly regret. Your priorities may be different if you have an infra team. What should I look for in a text-to-image API for a SaaS app? Four things, in the order they'll actually hurt you. Model availability in your regions comes first. If you sell into both the US and the EU, check that the model you pick is served in both, because "we support Europe" sometimes means the marketing site and not the inference region. Ask for it in writing if the answer matters to your DPA. Commercial use terms come second, and they're the ones nobody reads until legal asks. Most of the big image models now permit commercial use of outputs, but the details differ on who owns the output, whether you can train on it, and what happens with likenesses and trademarks. Read the actual terms page for the model, not the aggregator's summary of it — aggregators route to several vendors and the upstream licence is what governs your PDF. Then pricing shape. Per-image billing is easy to model in a spreadsheet; per-s
AI 资讯
One OpenAI-Compatible Endpoint for Multiple LLM Providers: A Practical Setup Guide
When an application starts using more than one language model provider, the hard part is rarely the first API call. The hard part is everything that follows: separate credentials, different request shapes, provider-specific errors, billing dashboards, and model migrations scattered across the codebase. A useful way to reduce that surface area is to keep one OpenAI-compatible client contract and move provider choice into configuration. This guide shows the smallest working setup with Routara , plus the production checks I recommend before sending real traffic. 1. Keep the SDK, change the endpoint If your project already uses the OpenAI Python SDK, the client initialization is the only part that needs to change: import os from openai import OpenAI client = OpenAI ( api_key = os . environ [ " ROUTARA_API_KEY " ], base_url = " https://api.routara.ai/v1 " , ) response = client . chat . completions . create ( model = " deepseek-chat " , messages = [ { " role " : " user " , " content " : " Explain idempotency in two sentences. " } ], ) print ( response . choices [ 0 ]. message . content ) Store the key in an environment variable. Do not put it in browser code, a public repository, screenshots, or support messages. The same pattern works in Node.js: import OpenAI from " openai " ; const client = new OpenAI ({ apiKey : process . env . ROUTARA_API_KEY , baseURL : " https://api.routara.ai/v1 " , }); const result = await client . chat . completions . create ({ model : " deepseek-chat " , messages : [{ role : " user " , content : " Return one short test sentence. " }], }); console . log ( result . choices [ 0 ]. message . content ); 2. Treat model IDs as configuration Do not spread model names throughout the application. Put them in environment variables or a typed configuration object: model_id = os . environ . get ( " ROUTARA_MODEL " , " deepseek-chat " ) That makes model evaluation and rollback much safer. Routara's live model catalog is the source of truth for current availa
开发者
Experts warn current Starship heat shield tech is a "dead end" for rapid reuse
NASA has not made substantial investments in thermal protection research for decades.
AI 资讯
What Spain's Verifactu law actually does to your backend
Spain is putting a hash chain behind every invoice, and almost everything written about it so far has been written for accountants. This is the version for whoever has to ship it. The deadlines are January 1, 2027 for companies and July 1, 2027 for sole traders. If you read something last year that said 2026, that was true until RD-ley 15/2025 moved the whole calendar back twelve months. Software vendors have been on the hook since July 2025, which is a detail worth holding on to if you sell a product that issues invoices for other people. At BeeL., we sell an API for this, so read the rest with that in mind. The requirement Each invoice your software issues has to produce a registro de facturación de alta: a record containing a defined set of fields, hashed with SHA-256, where the hash of each record folds in the hash of the one before it. One chain per issuing tax ID, growing forever, never edited. Cancelling an invoice is not a delete. It's a second record type, a registro de anulación, which goes into the same chain. Same for corrections, which come in two flavours depending on whether you're amending a difference or replacing the original document. The printed invoice carries a QR code with verification data, plus the string VERI*FACTU if you're in submitting mode. Then you either push each record to the tax agency as it happens, or you keep everything locally under stricter signing and retention rules and hand it over when asked. Written down like that, it reads like an afternoon of work. A hash function, a previous_hash column, an HTTP call. Where the estimate falls apart The chain is strictly sequential, so two workers issuing invoices for the same tax ID at the same time are racing for the same link. You need a lock per issuer, or a queue, or both, and either way concurrent issuance stops being free. Retries are worse than they look. A failed submission that you retry carelessly either duplicates a record or breaks the chain, and a broken chain isn't someth
AI 资讯
Passkeys Explained Simply
You’ve probably seen that little prompt that says “Sign in with Face ID” or “Use a passkey” instead...
AI 资讯
I needed Markdown JSON in four pipelines, so I shipped one endpoint that does it once
The same parser, four times Over the last year I kept running into the same shape of problem: A docs site generator that wanted Markdown chapters turned into navigation JSON. A RAG ingestion script where each Markdown file needed to become a list of text chunks plus its frontmatter metadata. An n8n flow that took Markdown emails and extracted only the tasklists. A static-site backend that accepted user Markdown and needed to validate structure before persisting. Each one is small on its own. But every time I reached for a different library — remark here, gray-matter there, marked once, a hand-rolled regex once too many — and every time one of them broke on the same edge cases: Nested GFM tasklists where the checked state was silently lost YAML frontmatter that included quoted booleans (parsed as strings, not booleans) Tables whose headers contained spaces (regex parsers treated them as one key) Code blocks containing Markdown — re-parsed as Markdown instead of fenced code So I built one endpoint that does it once, properly. What it returns POST /v1/parse takes a Markdown body ( text/markdown ) or a JSON envelope ( application/json ) and returns one stable JSON shape: { "success" : true , "data" : { "title" : "Project Alpha" , "frontmatter" : { "title" : "Project Alpha" , "status" : "shipping" }, "headings" : [ { "level" : 1 , "text" : "Project Alpha" , "id" : "project-alpha" } ], "sections" : [ { "heading" : { ... }, "children" : [ ... ], "content" : [ ... ] } ], "lists" : [ { "ordered" : false , "items" : [ "ship MVP" , "write README" ] } ], "tasklists" : [ { "items" : [ { "text" : "ship MVP" , "checked" : true } ] } ], "tables" : [ { "headers" : [ "Module" , "Status" ], "rows" : [{ "Module" : "API" , "Status" : "Done" }] } ], "codeBlocks" :[ { "lang" : "js" , "value" : "..." } ], "links" : [ { "text" : "..." , "url" : "https://..." } ], "paragraphs" :[ "..." ], "ast" : null } } The sections tree is the part I care most about. It's not just a flat list of headings
AI 资讯
A Secure Framework for Exposing SaaS Data to Your Data Lake
How to pull large volumes of data out of any enterprise SaaS platform — safely, repeatably, and without a single write permission. Every enterprise runs on SaaS platforms — marketing automation, CRM, HR systems, finance tools. And every data team eventually gets the same request: "Can we get that data into our lake?" The naive answer is to grab an admin's credentials, hit the API, and start downloading. It works — right up until the admin leaves the company, the password rotates, someone accidentally writes data back into the source system, or the security team asks who exactly has been exporting customer records at 2 AM. This post describes a framework I've used to expose SaaS platform data to a data lake the right way. It's platform-agnostic: the same pattern works for almost any modern SaaS tool that offers a REST API. The framework has four pillars: A least-privilege, read-only API role A dedicated, non-human service account OAuth 2.0 client-credentials authentication An asynchronous bulk-export job pattern Let's walk through each. Pillar 1: A Read-Only API Role Before touching any code, create a dedicated permission role inside the source platform — and give it only read permissions, only on the API surface. Most enterprise SaaS platforms separate permissions into two planes: UI permissions — what a human can click on in the web interface API permissions — what a token can do programmatically Your extraction role should have zero UI permissions and only the Read-Only API permissions for the objects you need: records, activities, memberships, whatever your platform calls them. Why this matters: Blast radius. If the credentials ever leak, the worst an attacker can do is read the same data you were already reading. They cannot delete records, trigger campaigns, or modify configuration. Auditability. When the security team reviews the role, "read-only, API-only" is a one-line conversation. Future-proofing. Ticking all the read-only permissions (rather than the two
AI 资讯
Why I Put Mirth Connect in Front of FastAPI Instead of Parsing HL7 in Python
When I started building my Maternity HL7-to-FHIR Pipeline , my first instinct was to do everything in Python. Parse the HL7 message, map the fields, validate the FHIR resource, persist it, all in one FastAPI service. It was clean. It was simple. It was wrong. The "Just Parse It in Python" Phase My initial architecture looked like this: Hospital System --MLLP--> Python Script --> HAPI FHIR Server I used python-hl7 to split messages on | and count field positions. For a single ADT^A01 (patient admission) message, it worked fine. I could pull the patient name from PID-5 , the MRN from PID-3 , the gender from PID-8 , and build a FHIR Patient resource from it. Then I tried a real-ish maternity workflow (an admission, an order, and a set of vitals) and things fell apart quickly. Five Problems That Changed My Mind 1. MLLP Is Not HTTP Hospital systems don't send HL7 over HTTP. They send it over MLLP (Minimum Lower Layer Protocol), which is a TCP socket protocol with specific framing bytes ( \x0b at the start, \x1c\x0d at the end). The sender expects an ACK or NACK response in HL7 format, not an HTTP status code. Building an MLLP listener in Python is possible . Libraries like aioml7 exist. But you're now maintaining a custom TCP server alongside your HTTP API server, handling connection pooling, timeouts, and HL7 acknowledgment generation. That's a lot of infrastructure code that has nothing to do with your actual transformation logic. Mirth Connect handles MLLP natively. You point it at a port, it listens, it parses, it ACKs. Done. One config screen, no custom code. 2. HL7 Parsing Is Messier Than It Looks The pipe-delimited format looks simple: PID|1||1234567^^^MRN||TEST^PATIENT^MARY^^MS||19920315|F|||14 SAMPLE ST^^SYDNEY^NSW^2000^AU But consider: Component separators : PID-5 is TEST^PATIENT^MARY^^MS , which is family, given, middle, suffix (empty), prefix. Miss the empty suffix and your prefix ends up as the suffix. Repeating fields : PID-3 can contain multiple identifier
AI 资讯
Claude Opus 5 leads on agentic work — and undercuts Fable 5 on cost
Claude Opus 5 is out, and Artificial Analysis — who supported Anthropic's pre-release evaluation — just dropped their full benchmark breakdown. The headline: new top model for agentic knowledge work, and cheaper per task than Fable 5. That combination doesn't come along often at the frontier. "Opus 5 (max) scores 61 on the Artificial Analysis Intelligence Index, effectively tied with Claude Fable 5 (max, 60), and ahead of GPT-5.6 Sol (max, 59)" What actually changed New agentic leader: 1861 Elo on GDPval-AA v2 — more than 100 points ahead of both Fable 5 and GPT-5.6 Sol. On AA-Briefcase (agentic knowledge work), it's +146 Elo over Fable 5. Joint first on coding: Opus 5 (xhigh) with Claude Code tops the Artificial Analysis Coding Index, including the highest score on SWE-Atlas-QnA. 89% on Terminal-Bench v2.1: Roughly in line with the current terminal leader, GPT-5.6 Sol. Cost per task: $2.03 at max effort — vs Fable 5's $2.75. That's 26% less for equivalent or better intelligence on agentic benchmarks. 1M token context window (same as Opus 4.8), 5 effort settings (low → max), and server-side fallback support. Pricing: $5/$25 per million input/output tokens — same rate as previous Opus launches. The cost-intelligence shift For agentic workloads — the things most teams are actually building on right now — Opus 5 doesn't just match Fable 5. It beats it, and charges less to do it. Fable 5 was the "throw more at it" option. Opus 5 reframes the trade-off: better agentic outcomes and a lower bill. At mid-tier effort settings (high, xhigh), it can outperform both Opus 4.8 and Sonnet 5 on a cost-per-task basis. That's a lot of headroom to play with before you're even at max effort. The caveat worth flagging: factual knowledge still lags. Opus 5 improved +7 points on AA-Omniscience over Opus 4.8, but its hallucination rate climbed 14 points to 50% — it guesses more confidently when uncertain. For retrieval-heavy or factual precision tasks, Fable 5 still holds the edge. What to
AI 资讯
Every EnvCastError Tells You How to Fix It: Designing Error Messages as a Feature
int(os.environ.get("PORT", "8080")) fails constantly in ways that waste your time: ValueError: invalid literal for int() with base 10: 'abc' . No variable name. No hint about what a valid value looks like. You grep the codebase for PORT to even find where the read happened. specenv is a zero-runtime-dependency Python library for typed environment variable loading — casting, validation, schema grouping, prefix namespacing. All of that is useful, but none of it is the actual design decision worth writing about. The decision that shaped everything else was: every error must name the variable and say how to fix it, unconditionally, with no opt-out. Decision 1: The error message is generated at the failure site, not templated afterward It would be easy to build one generic EnvCastError(var_name, raw_value, target_type) and format a message from those three fields in __str__ . specenv doesn't do that — each cast failure builds its own message inline, at the point where the specific failure is known: if cast_type is int : try : return int ( raw ) except ValueError : raise EnvCastError ( f ' Cannot cast { name } = { raw !r} to int. \n ' f ' → Set { name } to a valid integer (e.g. { name } =8080) ' ) from None if cast_type is bool : ... raise EnvCastError ( f ' Cannot cast { name } = { raw !r} to bool. \n ' f ' → Set { name } to one of: 1/0, true/false, yes/no, on/off ' ) The generic version would produce "Cannot cast PORT='abc' to int" and stop there. The inline version gets to add (e.g. PORT=8080) for ints, 1/0, true/false, yes/no, on/off for bools, a namespaced hint for prefixed variables — because at the point of failure, you know exactly what a correct value looks like for that type, and a generic formatter three calls up the stack doesn't. The cost is a few lines of duplication across _caster.py 's type branches. That's a fair trade for every single error message being genuinely actionable instead of generically accurate. Decision 2: Missing-and-required collapses to t
AI 资讯
Swagger docs from your existing TypeScript types — no framework required
The problem Recently I was looking for an npm package to generate OpenAPI (Swagger) documentation for my existing TypeScript project. My biggest requirement was TypeScript type-to-schema conversion: I already have all my request and response types, so why should I maintain the same schemas again in OpenAPI? Zod support would be a nice bonus. After trying most of the existing solutions, I found they generally fall into two categories: 1. Runtime frameworks The most popular example is tsoa . Honestly, tsoa is one of the best OpenAPI generators available today: it understands TypeScript well, generates schemas automatically and detects status codes. There are also contract-first libraries like ts-rest , Zodios and express-zod-api . However, none of these solutions are agnostic when it comes to how you write your code — they all dictate the shape of your routes. 2. Manual generators The best-known example is swagger-jsdoc. You write raw OpenAPI next to your code in JSDoc comments: /** * @openapi * /users: * post: * ... */ swagger-jsdoc is simple and framework-agnostic, but it's too verbose and knows nothing about your types. Then I found a similar tool that solved the verbosity problem: @visulima/jsdoc-open-api . It parses much more readable JSDoc tags: /** * POST /users * @summary Creates a new user. * @tags Users * @bodyContent {User} application/json - User object to create. * @bodyRequired * @response 201 - User created successfully. * @responseContent {User} 201.application/json - The created user object. */ But it still doesn't care about your types. User here is just the name of a component you have to define elsewhere in your document. Why not parse the actual types at generation time? That question inspired me to create Autoswag . The solution - Autoswag Describe your routes with readable JSDoc, and let the generator convert your TS types along the way. It doesn't affect your runtime code in any way, works with any framework, and even with vanilla JS. This is w
AI 资讯
Has an API ever silently changed its response shape and broken your app before you noticed?
I keep running into (and hearing about) a specific kind of bug that never throws an error — an API you depend on quietly changes its response shape. A field disappears. A number becomes a string. Something that was always present is suddenly null. Nothing crashes immediately. It just produces wrong or missing data somewhere downstream, and you find out from a bug report, not a log. I'm curious how common this actually is outside my own experience, so — genuine question, not a pitch: Has this happened to you, with a third-party API or even an internal one your own team owns? How did you find out it happened — a user report, a stack trace somewhere unrelated, manual debugging? Do you currently do anything to catch this kind of thing before it bites you (contract tests, monitoring, or just... hoping)? If you don't do anything about it today, is that because it's not painful enough to bother, or because you just haven't found a lightweight way to? Not selling anything here, just trying to understand how real and how painful this actually is for people building on top of APIs day to day. Would genuinely appreciate hearing your experience, even a one-line "yeah this happened to me once, wasn't a big deal" is useful data.
AI 资讯
Your OpenAPI spec is already a test plan — here's how to turn it into Playwright tests automatically
If you're writing Playwright API tests manually from an OpenAPI/Swagger spec, you're doing work that should be automated. Every endpoint in your spec already tells you: What the request looks like (path, method, parameters, body schema) What responses to expect (200, 401, 404, 422...) What fields are required What security is needed That's not documentation — it's a test plan. You're just not running it yet. What I built I got tired of the boilerplate loop: read spec → write happy path → add 401 test → add missing-field test → repeat for 40 endpoints. So I built a tool that does it for you. swagger-to-playwright.vercel.app takes your OpenAPI 3.x spec (YAML or JSON) and generates a ready-to-run Playwright .spec.ts file. For each endpoint, it produces four tests: 1. Happy path — calls the endpoint with valid data, asserts 2xx response and key fields in the body. 2. Auth check — if your spec declares a security scheme, it calls without a token and asserts 401. Only generated when the spec actually says authentication is required — no false positives. 3. Input validation — sends a request with missing required fields (or wrong types, invalid enums) and asserts 422. Reads directly from your schema's required array and field types. 4. Contract validation — if there's a path parameter, it calls with an invalid value and asserts 404. What the output looks like Here's what you get for a POST /users endpoint with email and password required: import { test , expect } from ' @playwright/test ' ; test . describe ( ' POST /users ' , () => { test ( ' happy path — creates user successfully ' , async ({ request }) => { const res = await request . post ( ' /users ' , { data : { email : ' test@example.com ' , password : ' password123 ' } }); expect ( res . status ()). toBe ( 201 ); const body = await res . json (); expect ( body ). toHaveProperty ( ' id ' ); }); test ( ' auth — 401 without token ' , async ({ request }) => { const res = await request . post ( ' /users ' , { headers : {