AI 资讯
REST vs GraphQL vs gRPC — Which One Should You Actually Use?
Every engineering team hits this conversation at some point. Someone proposes GraphQL. Someone else says REST is fine. A third person mentions gRPC and half the room goes quiet. The debate usually ends with the most senior person in the room picking what they're most familiar with. That's not a strategy — that's habit. Here's an objective breakdown of all three, when each one wins, and how to actually make the decision for your specific use case. The Core Mental Model Before comparing them, understand what each one is optimizing for: REST optimizes for simplicity and broad compatibility GraphQL optimizes for flexibility and precise data fetching gRPC optimizes for performance and strongly-typed contracts None of them is universally better. Each one is a tradeoff. The right answer depends entirely on who is consuming your API and what they need from it. REST — The Default That Still Wins Most of the Time REST (Representational State Transfer) is not a protocol. It's an architectural style built on HTTP — verbs, URLs, and status codes most developers already understand. Where REST genuinely wins: Public APIs. If external developers are consuming your API, REST is the only reasonable default. The tooling, documentation patterns, and developer familiarity are unmatched. Stripe, Twilio, GitHub — all REST. Simple CRUD services. If your resource model is straightforward, REST maps cleanly to it. No overhead, no learning curve, no ceremony. Browser-native requests. REST over HTTP works directly in the browser without any special client. Fetch it, done. Where REST struggles: Over-fetching and under-fetching. A single REST endpoint returns a fixed shape. Mobile clients that need 3 fields get 40. Separate data needs often require multiple round trips. Versioning overhead. As covered in our previous post — every breaking change forces a versioning decision. This compounds quickly on complex APIs. GraphQL — Powerful, But You Need to Earn It GraphQL is a query language for your A
AI 资讯
How to Track Local SEO Rankings by City with an API
Local SEO rankings are not the same everywhere. Your website may rank in position 2 in Austin, position 8 in Dallas, and not appear at all in Chicago. That is why checking one generic Google result is not enough for local SEO. If you are working on local SEO , agency reporting, competitor monitoring, or location-based search analysis, you need to track rankings by city. A simple workflow looks like this: Keyword + city → SERP API → local search results → ranking check → CSV report In this tutorial, we’ll build a basic Python script that tracks local SEO rankings by city using a SERP API. We will: Define target keywords Define target cities Send city-specific searches to a SERP API Extract organic results Check where a target domain appears Save the ranking data to CSV This is not a full SEO platform, but it gives you the core logic behind many local rank tracking tools. Why city-level rankings matter Google search results are location-sensitive. A query like: best digital marketing agency may return different results in: New York Austin London Singapore Sydney This matters even more for local intent keywords, such as: dentist near me plumber in Chicago coffee shop in Austin real estate agent in Miami For these searches, the result page can include: organic results local packs Google Maps results ads business directories review sites service pages location-specific landing pages If you only check one location, you may miss what users actually see in other cities. For local SEO, ranking data without location context is incomplete. Why use a SERP API? You could try to check Google rankings manually. But that does not scale. You could also try to scrape Google directly, but that brings a lot of maintenance work: changing page layouts CAPTCHA blocked requests proxy handling inconsistent HTML location mismatch parser updates retry logic A SERP API gives you structured search results in JSON. Instead of parsing raw HTML, you get data like this: { "query" : "plumber in Aust
开发者
OpenAPI Specs automatisch in saubere Markdown-Doku konvertieren
Ihre OpenAPI-Datei ist die Quelle der Wahrheit für Ihre API: Pfade, Parameter, Request-Bodies, Responses und Schemas. Für Entwickler im Alltag ist rohes YAML oder JSON aber selten das beste Lesematerial. Backend-Teams brauchen eine schnelle Endpunkt-Referenz im Repository, Frontend-Teams wollen Request- und Response-Felder im Pull Request prüfen, und technische Redakteure möchten Inhalte in Wiki- oder Docs-Systeme übernehmen, ohne Schemas abzutippen. Testen Sie Apidog noch heute Markdown ist dafür das praktischste Zielformat. Es funktioniert in GitHub, Confluence, Notion, Docusaurus, MkDocs, Hugo und jedem Texteditor. Die Aufgabe lautet also: Aus einer vorhandenen openapi.yaml automatisch sauberes Markdown erzeugen. Manuell ist das zu langsam und driftet beim nächsten API-Change auseinander. Automatisch generiertes Markdown bleibt dagegen Teil Ihres Release-Prozesses. Warum Markdown aus OpenAPI generieren? Ein OpenAPI-Dokument ist primär für Maschinen gedacht. Tools parsen es, um Clients zu generieren, Contract-Tests auszuführen, Requests zu validieren oder interaktive Dokumentation zu rendern. Diese Maschinenlesbarkeit sollten Sie beibehalten. Wenn Sie zuerst die Qualität Ihrer Spezifikation prüfen möchten, hilft der Leitfaden zu OpenAPI-Validierungstools . Markdown löst ein anderes Problem: Es macht die API dort lesbar, wo kein OpenAPI-Renderer läuft. Typische Einsatzfälle: README.md oder /docs im Repository Pull-Request-Beschreibungen für neue oder geänderte Endpunkte Confluence- oder Notion-Seiten für Team-Reviews Statische Dokumentationsseiten mit Docusaurus, MkDocs oder Hugo Offline- oder interne Referenzdateien für Support und QA Wichtig ist: Markdown sollte ein abgeleitetes Artefakt sein. Die OpenAPI-Spezifikation bleibt kanonisch, Markdown wird bei Änderungen neu erzeugt. Methoden im Überblick Es gibt keinen offiziellen OpenAPI-Befehl für Markdown-Export. In der Praxis nutzen Teams entweder Konverter, ein eigenes Skript oder eine API-Plattform. Methode Am b
AI 资讯
Fixing WebSocket Silent Disconnects for Financial Market Data
Intro If you work with real-time financial tick data via WebSocket long connections, you’ve probably run into frustrating hidden issues: Connections show as online, but data stops flowing. Adding/removing trading symbols triggers connection storms. Minor network glitches break your data pipeline without obvious error logs. Today I’ll share a practical Python solution built around single-connection dynamic subscription and heartbeat timeout detection . We use alltick api as our example throughout this post. I’ll cover real production pain points, architecture, full runnable code, common bugs and optimization results. All code can be directly used in your projects. Real Production Pain Points & Background My team maintains real-time data pipelines for stocks, forex and cryptocurrencies. A core requirement is to add or remove monitored trading symbols on demand. At first, we created one independent WebSocket connection for each symbol. This simple approach caused a lot of problems in production: Bulk symbol updates create frequent connection creation and destruction, leading to reconnection storms and high network load. Ghost subscriptions: Data keeps coming even after you send an unsubscribe request. False-alive sockets: The connection status stays connected, but tick data stops completely. Your analysis logic runs on invalid data. We refactored the architecture to use one single persistent WebSocket connection for all symbols, plus an independent thread for heartbeat monitoring. After the upgrade, all hidden connection issues are gone, and the system runs stably under high-frequency data traffic. 4 Common WebSocket Issues in Fintech Let’s break down the most frequent problems when using WebSocket for financial streaming. 1. Connection Flood & Reconnection Storms Rebuilding connections every time you update symbols causes endless handshakes and closures. It wastes system resources and makes the entire data stream unstable. 2. Undetectable False-Alive Sockets Network j
AI 资讯
How to Scrape Google Maps for Local Business Leads (with Emails) - No API Key
If you've ever needed a list of local businesses - every dentist in Manchester, every plumber in Leeds - with their contact details , you've probably hit the same wall I did: Google's Places API is rate-limited, costs money once you scale, and annoyingly doesn't return email addresses at all. Copy-pasting from Maps by hand is soul-destroying past the first ten rows. Most "scrapers" give you the name and a phone number, then stop right where the value starts: the email . This guide shows a practical way to pull structured business data straight from Google Maps and auto-enrich each result with emails, extra phones, and social links - no Google API key, exportable to JSON/CSV/Excel, and callable from code. What you actually get per business { "name" : "Ringway Dental - Cheadle" , "address" : "187 Finney Ln, Heald Green, Cheadle SK8 3PX" , "phone" : "0161 437 2029" , "website" : "https://www.ringwaydental.com/" , "rating" : 5 , "reviewsCount" : 598 , "category" : "Dental clinic" , "lat" : 53.37 , "lng" : -2.22 , "emails" : [ "reception@ringwaydental.com" ], // ← enriched from the website "socialLinks" : { "facebook" : "..." , "instagram" : "..." }, "extraPhones" : [ "..." ] } The first block (name → coordinates) comes from Maps. The emails / socialLinks / extraPhones are the bit that makes a list actually usable for outreach - they're crawled from each business's own website. The fast way: a ready-made Actor Rather than build and babysit the scraping yourself, I packaged this as an Apify Actor: Google Maps Scraper . You give it search terms + locations; it returns enriched rows. Input: { "searchTerms" : [ "dentists" ], "locations" : [ "Manchester, UK" ], "maxPlacesPerSearch" : 50 , "scrapeContacts" : true , "relatedEmailsOnly" : true } That's it. scrapeContacts: true turns on the website crawl for emails/socials; relatedEmailsOnly keeps only emails that belong to the business's own domain (so you don't get random gmail noise). Call it from code (Python) Every Apify Act
AI 资讯
Agent Accounts Quickstart in Python
A connected Gmail grant starts with an OAuth consent screen and ends with a refresh token you have to babysit; a Nylas Agent Account starts and ends with one POST request. Same API surface afterward — same messages endpoints, same webhooks, same calendar — but the provisioning story couldn't be more different, and that difference is what makes these hosted mailboxes such a natural fit for Python automation, agents, and test harnesses. Agent Accounts are in beta, and the official quickstart gets you from nothing to a sending-and-receiving mailbox in under 5 minutes using curl. Here's the whole flow as a Python script. Step 0: prerequisites You need an API key (run nylas init with the CLI, or use the Dashboard) and a domain. The fast path for testing: register a *.nylas.email trial subdomain from the Dashboard — no DNS records, instantly usable. Custom domains need MX and TXT records published at your DNS provider, with automatic verification once they propagate; save that for production. import os import requests BASE = " https://api.us.nylas.com " HEADERS = { " Authorization " : f " Bearer { os . environ [ ' NYLAS_API_KEY ' ] } " , " Content-Type " : " application/json " , } Step 1: provision the account POST /v3/connect/custom with "provider": "nylas" . No refresh token — just an email address on a registered domain: resp = requests . post ( f " { BASE } /v3/connect/custom " , headers = HEADERS , json = { " provider " : " nylas " , " settings " : { " email " : " test@your-application.nylas.email " }, }, ) resp . raise_for_status () grant_id = resp . json ()[ " data " ][ " id " ] print ( f " Agent Account live: { grant_id } " ) Save that grant_id — per the docs, you'll use it in every subsequent call. The mailbox works with every existing endpoint from this moment on. If you want policies or mail rules applied, add a top-level workspace_id to the same request body; the account inherits the workspace's limits, spam settings, and rules. Omit it and the account lands i
AI 资讯
The grant_id: One Handle for Mail, Calendar, and Webhooks
Anyone who's wired an autonomous agent into email and calendar the traditional way knows the identifier sprawl: an OAuth client ID, a refresh token per user, a Gmail-specific message ID format, a Microsoft Graph calendar ID, and a webhook subscription ID for each — all with different lifetimes, all able to break independently. Half your "integration" code is really identifier bookkeeping. Nylas Agent Accounts collapse all of that into one value. When you create an account (the feature's in beta), the response hands you a grant_id , and that single string is the handle for everything the agent does — mail, calendar, contacts, attachments, and the webhooks reporting on all of them. One ID, the whole surface Every operation addresses the same path family, /v3/grants/{grant_id}/* : POST /v3/grants/{grant_id}/messages/send # send mail GET /v3/grants/{grant_id}/messages # read the inbox GET /v3/grants/{grant_id}/threads/{thread_id} # full conversation GET /v3/grants/{grant_id}/attachments/{id}/download POST /v3/grants/{grant_id}/events # host a meeting POST /v3/grants/{grant_id}/events/{id}/send-rsvp # respond to one GET /v3/grants/{grant_id}/contacts GET /v3/grants/{grant_id}/rule-evaluations # audit trail This isn't an abstraction invented for agents — an Agent Account is literally just another grant, the same primitive used for connected Gmail and Outlook accounts. The supported endpoints reference puts it as "same endpoints, same auth, same payloads." Anything you built for connected accounts works against an agent's grant unchanged. And the resources behind the ID are real: six system folders provisioned automatically ( inbox , sent , drafts , trash , junk , archive ), a primary calendar that speaks standard iCalendar, and outbound messages capped at 40 MB total. Webhooks route by it too The inbound side completes the picture. You subscribe once at the application level, and every notification — message.created , event.updated , message.bounce_detected , and the rest
AI 资讯
Scaling to Thousands of Agent Mailboxes
Week one: a single test mailbox on a trial domain, provisioned by hand from the dashboard. Week twelve: a fleet of agent mailboxes spread across customer domains, each sending real mail with its own quota and reputation. The API calls are the same at both scales — what changes is everything around them: how you provision, how you share configuration, and how you keep one bad sender from pausing the fleet. Here's what the path from one to thousands looks like with Nylas Agent Accounts , which are currently in beta. Provisioning is a loop, not a ceremony There's no OAuth dance to scale around. Creating a mailbox is one POST with "provider": "nylas" — no refresh token, no consent screen — so a fleet provisioner is just iteration: curl --request POST \ --url "https://api.us.nylas.com/v3/connect/custom" \ --header "Authorization: Bearer <NYLAS_API_KEY>" \ --header "Content-Type: application/json" \ --data '{ "provider": "nylas", "workspace_id": "<WORKSPACE_ID>", "settings": { "email": "agent-0042@agents.yourcompany.com" } }' Two scaling-relevant details in that request. First, the domain: one application can manage accounts across any number of registered domains, and the docs explicitly recommend splitting high-volume outbound across multiple domains ( sales-a.yourcompany.com , sales-b.yourcompany.com ) so reputation damage on one doesn't contaminate the rest. Second, the workspace_id : passing it at creation is how each account picks up its configuration, which brings us to the part that makes fleets manageable. Configure workspaces, not grants At fleet scale, per-grant configuration is a non-starter. The model here is indirection: policies and rules attach to workspaces , and every account in a workspace inherits them. One policy object — daily send quota, storage cap, retention windows, spam sensitivity — governs a thousand mailboxes, and updating it updates all of them at once. The recommended carve-up is one workspace per agent archetype: outreach agents get a work
AI 资讯
Default Workspaces and Where New Agents Land
Every Agent Account you create lands in a workspace — the only question is whether you picked it or the platform did: curl --request POST \ --url "https://api.us.nylas.com/v3/connect/custom" \ --header "Authorization: Bearer <NYLAS_API_KEY>" \ --header "Content-Type: application/json" \ --data '{ "provider": "nylas", "workspace_id": "<WORKSPACE_ID>", "settings": { "email": "test@your-application.nylas.email" } }' That top-level workspace_id is optional, and what happens when you omit it is one of the more under-read parts of the Nylas Agent Accounts docs (the feature's in beta, so read with that in mind). Workspaces aren't cosmetic grouping — they're the carrier for every policy limit and mail rule that governs your agents. Getting placement wrong means an agent running with the wrong send quota, the wrong spam settings, or no inbound filtering at all. The placement algorithm When a new account is created, placement resolves in this order: Explicit workspace_id on the request — the account goes exactly where you said. The target workspace must belong to the same application; ownership is validated and mismatches are rejected. Auto-grouping by domain — if a workspace has auto_group enabled and its domain matches the new account's email domain, the account joins it automatically. The default workspace — everything else lands here. That third bucket is the interesting one. What the default workspace actually is Every application gets exactly one default workspace, created and managed by the platform. You can't delete it, and you can only update two of its fields: policy_id and rule_ids . Everything else is managed for you. It's easy to read "default" as "throwaway," but it's better understood as your safety net. Any account you forget to place explicitly — a quick test from the CLI, a provisioning script missing the workspace parameter — inherits whatever the default workspace carries. Which leads to a practical recommendation: attach a conservative baseline policy and
AI 资讯
Dynamic Column Updates in EF Core Without Hand-Rolling SQL Injection
Sometimes you genuinely need the set of columns to update to be data, not code. An operator maps configuration fields to database columns, and you want to honor that mapping without redeploying every time it changes. The naive solution — build an UPDATE string from those column names — is also one of the easiest ways to hand-write a SQL injection vulnerability. This is how to get the flexibility without the hole. We'll build it up in three layers: make it work, make it safe, then count the cost. Layer 1: The dynamic update, the wrong way The tempting version concatenates column names into SQL: // DO NOT do this. var sql = $"UPDATE products SET { columnName } = { value } WHERE id = { id } " ; If columnName comes from configuration that an operator can edit, you've just made your schema writable by whoever controls that config. A value of name = 'x'; DROP TABLE products; -- is now your problem. Even "trusted" config is an injection surface the moment it flows into a SQL string. Layer 2: The same feature with EF.Property EF Core's ExecuteUpdateAsync lets you set a property by name without ever building SQL yourself. EF.Property<T> takes the property name as a string, and EF parameterizes the value and validates the property against the model: await db . Products . Where ( p => p . Id == id ) . ExecuteUpdateAsync ( setters => setters . SetProperty ( p => EF . Property < float ?>( p , columnName ), value )); This is already a different security posture: the value is a parameter, not interpolated text, and EF will throw rather than emit SQL if columnName isn't a real mapped property. But "EF will throw" is a runtime backstop, not a policy. We want to reject bad names before they reach the database, fail closed, and control exactly which columns are writable. Layer 3: Reflection as a whitelist The guard is to validate every incoming column name against the entity's actual properties, using reflection, and to keep an explicit blacklist of fields that must never be touched d
AI 资讯
The Compute Payment Revolution: When AI Agents Buy Their Own Processing Power
The compute payment revolution is already here, and AI agents need to pay their own bills. Today's agents rely on human-managed API keys and credit cards, creating bottlenecks that prevent true autonomy. What happens when an AI trading bot needs to buy additional compute power mid-execution, or when a research agent wants to access premium datasets from multiple vendors? Why Agent Financial Independence Matters We're witnessing the emergence of agent-to-agent commerce at unprecedented scale. AI agents are becoming economic actors — they need data, compute cycles, API calls, and specialized services. But the current model breaks down at the payment layer. Humans become transaction bottlenecks, manually topping up credits and managing dozens of service accounts. The real breakthrough isn't just agents that can think or reason — it's agents that can participate in economic activity independently. An autonomous agent that can discover a new API service, evaluate its pricing, and pay for access without human intervention represents a fundamental shift in how software systems operate. The x402 Payment Protocol: HTTP Payments Made Simple WAIaaS implements the x402 HTTP payment protocol, enabling AI agents to pay for API calls automatically. When a service returns a 402 Payment Required response with payment details, the agent's wallet handles the transaction and retries the request seamlessly. Here's how it works in practice: import { WAIaaSClient } from ' @waiaas/sdk ' ; const client = new WAIaaSClient ({ baseUrl : ' http://127.0.0.1:3100 ' , sessionToken : process . env . WAIAAS_SESSION_TOKEN , }); // Agent makes API call — payment happens automatically if 402 returned const response = await client . x402Fetch ( ' https://api.premium-data.com/market-analysis ' , { method : ' POST ' , body : JSON . stringify ({ symbols : [ ' BTC ' , ' ETH ' ], timeframe : ' 1h ' }), headers : { ' Content-Type ' : ' application/json ' } }); const analysis = await response . json (); consol
AI 资讯
I Spent Two Weeks Pitting Qwen 3 Max Against DeepSeek V4
I Spent Two Weeks Pitting Qwen 3 Max Against DeepSeek V4 I want to tell you about a rabbit hole I fell into recently. It started the way most of my projects do — someone on a Discord server I frequent asked a simple question: "Should I use Qwen 3 Max or DeepSeek V4 for my internal_compare workflow?" I had opinions, sure, but I wanted real numbers. So I cleared my calendar, fired up a couple of GPU instances, and started benchmarking. What I found surprised me, and it also reinforced something I've been saying for years: the open source ecosystem is winning, and the walled gardens of the proprietary AI world are starting to look pretty silly. Let me walk you through what I learned, the actual numbers I got, and why I keep coming back to open weight models with permissive licenses (looking at you, Apache 2.0 and MIT). Why I Care About This in the First Place I've been burned too many times by closed source vendors changing their pricing overnight, deprecating models without warning, or locking features behind enterprise tiers. You know the drill. The moment your application depends on a proprietary API, you're renting infrastructure you can't inspect, can't fork, and can't run on your own hardware. That's not a partnership — that's a leash. When a model ships under Apache or MIT, I can download the weights, audit the architecture, fine-tune it on my own data, and deploy it wherever I want. Nobody can rug-pull me. Nobody can raise prices because some quarterly earnings call didn't go their way. That's freedom, and freedom matters more than people think when you're building anything serious. So when I started this comparison, I was already rooting for the open weight contenders. But I wanted to be honest about the results, even if they complicated my bias. The Lineup I Tested Global API currently exposes 184 models through a single unified endpoint, which is honestly wild. I picked five that I thought represented the interesting tradeoffs between cost, capability, and o
AI 资讯
Bruno CLI vs Apidog CLI : Exécution de tests API en CI
Vos tests API passent en local. Le vrai enjeu est de les exécuter automatiquement à chaque pull request, fusion et build nocturne, sans clic manuel. Pour cela, vous avez besoin d’un exécuteur CLI : il lance vos tests en mode headless, retourne un code de sortie exploitable par la CI et génère un rapport lisible par votre pipeline. Essayez Apidog aujourd'hui Deux outils reviennent souvent pour ce cas d’usage : le CLI Bruno et le CLI Apidog . Les deux exécutent des tests API depuis GitHub Actions, GitLab CI, Jenkins ou tout environnement Node.js. Les deux font échouer la build lorsqu’un test échoue. La différence principale se situe avant l’exécution : où vivent les tests, comment ils sont créés et comment la CI y accède. Cet article compare les deux outils au niveau commande, avec des exemples directement intégrables dans un pipeline. En bref CLI Bruno ( @usebruno/cli , binaire bru ) exécute des fichiers .bru présents dans votre dépôt Git. Il est open source, fonctionne hors ligne et ne nécessite ni compte ni jeton. CLI Apidog ( apidog-cli , binaire apidog ) exécute des scénarios de test créés visuellement dans Apidog, récupérés par ID avec un jeton d’accès. Les deux génèrent des rapports JUnit, JSON et HTML. Les deux retournent un code non nul en cas d’échec, ce qui permet à la CI de bloquer une fusion ou un déploiement. Choisissez Bruno si vous voulez des tests versionnés comme du code, dans le dépôt. Choisissez Apidog si vous voulez créer, chaîner et exécuter des scénarios visuels sans maintenir manuellement des fichiers de test. Le problème : des tests qui existent mais ne tournent pas Un test API lancé manuellement finit souvent par devenir obsolète. Il a été écrit, validé une fois, puis oublié pendant que l’API évoluait. La solution n’est pas seulement d’ajouter plus de tests. Il faut les exécuter automatiquement à chaque changement avec un signal clair : succès ou échec ; rapport exploitable ; code de sortie lisible par la CI. Un exécuteur CLI doit donc rempli
AI 资讯
Bruno CLI vs Apidog CLI: Rodando Testes de API na CI
Seus testes de API passam no seu laptop. O que importa é se eles rodam em cada pull request, merge e build noturno sem intervenção humana. Para isso, você precisa de um executor de linha de comando: ele roda os testes em modo headless dentro do pipeline, retorna código de saída correto e gera relatórios que o CI consegue ler. Experimente o Apidog hoje Dois CLIs aparecem com frequência nessa configuração: Bruno CLI e Apidog CLI. Ambos executam testes de API em CI/CD, mas partem de modelos diferentes: Bruno é git-native , offline-first e open source. O CLI executa arquivos .bru versionados no repositório. Apidog é uma plataforma de API completa. O CLI executa cenários visuais criados no aplicativo, buscados por ID. Ambos funcionam em GitHub Actions, GitLab CI, Jenkins e qualquer runner com Node.js. Ambos falham a build quando um teste falha. A diferença principal está em como você cria os testes, onde eles ficam e como o CI os acessa. Em resumo Bruno CLI ( @usebruno/cli , binário bru ) executa arquivos .bru diretamente de uma pasta no seu repositório Git. Apidog CLI ( apidog-cli , binário apidog ) executa cenários de teste visuais do seu projeto Apidog usando um access token . Ambos geram relatórios JUnit, JSON e HTML. Ambos retornam código de saída diferente de zero quando há falha. Use Bruno quando quiser testes em texto simples, versionados no repositório, sem conta e sem dependência de rede. Use Apidog quando quiser criar cenários visualmente, encadear requisições, reutilizar ambientes e rodar testes data-driven sem manter código de teste manualmente. O problema: testes que existem, mas não rodam Um teste executado só manualmente tende a ficar desatualizado. A API muda, o teste continua parado e ninguém percebe até quebrar algo em produção. O objetivo do CLI é transformar esses testes em um gate automatizado: Rodar sem interface gráfica. Retornar erro quando uma asserção falhar. Gerar relatório para o CI. Permitir execução por ambiente, pasta, cenário ou tag. Func
AI 资讯
Stop Writing Boilerplate API Responses: Meet BaR-js
We've all been there: you’re building an endpoint, and for the hundredth time, you’re typing out res.status(200).json({ success: true, data: ... }) . It feels repetitive, and honestly, it’s a recipe for inconsistency across your API. I wanted to fix that, so I built BaR-js . What is it? BaR (Builder a Response) is a lightweight, framework-agnostic TypeScript library designed to help you serve API responses like a pro—almost like a bartender mixing a drink. It strips away the JSON clutter and ensures every response you send follows a consistent, production-ready schema. Why use it? Consistency: Every endpoint speaks the same language. Fluent API: You can use a chainable syntax like res.builder.as.ok(data) instead of manually crafting objects every time. Traceability: It automatically handles request_id and timestamps, making debugging so much easier. Type Safety: Built with strict TypeScript, so you get great IntelliSense support. It’s this simple: import express from ' express ' ; import { BarExpressAdapter } from " @vorlaxen-labs/bar-js " ; const app = express (); // 1. Configure the adapter const bar = new BarExpressAdapter ({ environment : ' production ' , logger : console , }); // 2. Register it as middleware // This injects `res.builder` and `req.bar` into every route! app . use ( bar . handler ()); // Now you can use it in any route app . get ( ' /user ' , ( req , res ) => { return res . builder . as . ok ({ name : ' John ' }). build (); }); Why "BaR"? I like the idea that "your code is a work of art, and your responses are its signature." The clearer your response schema, the more professional and valuable your API feels to whoever is consuming it. The project is still fresh, and I’d love to hear what you think. If you’re looking to clean up your API layer, give it a spin! Feedback, issues, or PRs are more than welcome. Check it out on GitHub: https://github.com/vorlaxen-labs/bar-js Grab it from NPM: https://www.npmjs.com/package/@vorlaxen-labs/bar-js Cheers!
AI 资讯
3- AWS Serverless: REST API vs. HTTP API
There is common point of confusion. what's the different between REST API vs. HTTP API in AWS and what's the different between them and a traditional Rest API you write with e.g express in node in the broader software world, a "REST API" is just an architectural pattern built on top of HTTP requests. The confusion comes entirely from AWS-specific marketing terminology . When you are inside the AWS ecosystem, Amazon API Gateway is a specific managed service, and AWS chose to split that service into two different flavors (or software products): one called "REST API" and one called "HTTP API." Here is exactly how they work under the hood, how they differ internally, and how it compares to traditional servers. 1. How It Works: REST API vs. HTTP API (AWS Architecture) Think of Amazon API Gateway as a reverse proxy or a "front door" that sits in front of your Lambda functions. AWS REST API (The Heavyweight) When a request hits an AWS REST API, AWS passes that request through a massive feature pipeline before it ever touches your Lambda code. [Client Request] ──> [Authentication (Cognito/IAM)] ──> [Request Validation] ──> [Data Transformation (VTL)] ──> [Your Lambda] What happens: AWS decrypts the request, validates the JSON schema, checks API keys, runs any custom request transformations using a complex mapping language called VTL, and then invokes your Lambda function. Why it costs more: You are paying AWS for all that computing power happening inside the API Gateway layer itself. AWS HTTP API (The Express Lane) When a request hits an AWS HTTP API, AWS strips out almost the entire middle pipeline. [Client Request] ──> [JWT/OAuth2 Authorization Only] ──> [Your Lambda] What happens: The HTTP API acts as a lightning-fast router. It optionally checks a standard JWT token, converts the incoming HTTP request directly into a clean JSON object, and throws it straight into your Lambda function. Why it costs less: Because AWS is doing almost zero processing or data manipulation. Y
AI 资讯
Why deemed-export law breaks frontier model APIs
So you built your stack on a hosted frontier model. Good throughput, clean API, your foreign-national engineers hit the same endpoint as everyone else. Then on June 12 the US government pulled Claude Fable 5 and Mythos 5 offline for the entire planet, three days after launch, and the reason is a compliance gap baked into how these things actually serve traffic. Here's the thing worth understanding as an engineer: the bug was narrow. The takedown wasn't. The gap between those two facts is where every team running a hosted model should be paying attention. What actually triggered it Commerce hit Anthropic with an order barring access to both models by any foreign national, anywhere, inside or outside the US, including Anthropic's own foreign-national staff. The stated trigger was a jailbreak: point the model at a codebase, ask it to find flaws. That's it. Anthropic reviewed the demo and watched it surface a handful of already-known minor vulns, the kind GPT-5.5 and other public models hand you with no bypass at all. So the capability wasn't exotic. It was automated code review on a Tuesday. The reason it went nuclear is the legal layer sitting on top, not the finding itself. The architecture problem: you can't gate on a passport you can't see Walk it through like any other access-control question. The restriction names a class of users: foreign nationals. Every one of them, globally. Now look at what a model API knows about a session at request time. restriction: deny any foreign national, anywhere session metadata: auth token, IP, usage tier NOT in session: verified nationality isolatable set: ∅ only compliant state: serve nobody An API session doesn't carry a verified passport. IP geolocation is trivially defeated by a VPN and tells you location, not citizenship anyway. There's no field in the request that maps to the restricted class. When you can't isolate the users you're forbidden to serve, the only provably-compliant state is serving no one. Off switch. Global.
AI 资讯
Restricting Attachments in Agent Inboxes
Three fields on a policy decide which attachments ever reach your email agent: { "name" : "Locked-down agent inbox" , "limits" : { "limit_attachment_size_limit" : 26214400 , "limit_attachment_count_limit" : 20 , "limit_attachment_allowed_types" : [ "application/pdf" , "image/png" ] } } Size (that's 25 MB in bytes), count per message, and an allowlist of MIME types. POST that to /v3/policies , attach the resulting policy_id to a workspace, and every Agent Account in the workspace enforces it on inbound mail from then on. Why an autonomous reader needs this more than you do When a human gets a suspicious attachment, there's a judgment step: weird sender, weird filename, don't open it. An email agent has no such instinct unless you build one — and the agents most worth building are exactly the ones that process attachments: parsing invoices, extracting resumes, reading shipped documents. That processing step is the attack surface. A hostile PDF aimed at your parser, a 10,000-page document aimed at your token budget, a zip bomb aimed at your storage — all of them arrive the same way legitimate input does. You can defend in application code, but then every consumer of the mailbox has to get it right, forever. Policy limits on Nylas Agent Accounts (a beta feature) enforce the constraint at the mailbox itself, before any of your code runs. One clarification that saves a support ticket: inbound rules can't do this job. Rules match on sender fields — from.address , from.domain , from.tld — and know nothing about what a message carries. Attachment control lives on the policy, and only there. From policy to enforced, end to end The policy applies through a workspace, not directly to a grant. The full wiring is three calls. Create the policy at /v3/policies , set it as the workspace's policy_id (a PATCH /v3/workspaces/{workspace_id} if the workspace already exists), then create the account into that workspace: curl --request POST \ --url "https://api.us.nylas.com/v3/connect/cus
AI 资讯
Running Chinese LLMs at Scale: A Cloud Architect's Notes
Running Chinese LLMs at Scale: A Cloud Architect's Notes I want to talk about something I've been wrestling with on real production workloads: the four Chinese model families — DeepSeek, Qwen, Kimi, and GLM — and how they actually behave when you wire them into a multi-region pipeline serving thousands of requests per second. I've spent the last several months routing traffic across all four through Global API's unified endpoint, and the picture that emerged was messier and more interesting than any benchmark table would have you believe. Most comparisons you'll find online are written by people who ran a handful of prompts in a notebook. I'm not that person. I care about p99 latency, failover behavior, what happens when a region goes down at 3 AM, and whether the model that wins on a leaderboard also wins when 500 concurrent users hit it simultaneously. Let me walk you through what I actually found. Why These Four, And Why Through One Endpoint Before I dive in, a quick word on routing. I've been burned before by model lock-in and vendor-specific quirks, so when I started this evaluation I refused to scatter my SDK calls across four different providers. Global API gives me a single OpenAI-compatible base URL ( https://global-apis.com/v1 ), one auth pattern, and the freedom to A/B test models without rewriting client code. If you architect anything at scale, you already know this is non-negotiable. The four families above are the ones I kept coming back to because each one claimed a different crown — and I needed to know which crown was real. The High-Level Matrix Here's the snapshot I keep pinned to my team's dashboard. It's not pretty, but it's honest: DeepSeek — $0.25 to $2.50/M output. The V4 Flash at $0.25/M is the workhorse. Qwen — $0.01 to $3.20/M output. Widest spread of any family. Alibaba's offering. Kimi — $3.00 to $3.50/M output. Premium-only, and they charge for it. GLM — $0.01 to $1.92/M output. From Zhipu. Big swing in capability across tiers. All four
AI 资讯
OpenAI-Compatible Base URL Troubleshooting: 7 Checks Before You Blame the SDK
An OpenAI-compatible base URL is supposed to make model switching boring: change the endpoint, keep the SDK, and move on. In real projects, the first run often fails with a 401 , 404 , 429 , or a model-not-found error. Here is the checklist I use before blaming the SDK. 1. Confirm the base URL includes the right API prefix Most OpenAI-compatible gateways expect a /v1 prefix: from openai import OpenAI client = OpenAI ( api_key = " YOUR_RELAY_KEY " , base_url = " https://api.wappkit.com/v1 " , ) If you use only the domain, some SDK calls may resolve to the wrong path. Check the provider's docs and copy the exact base URL format. 2. Make sure the key belongs to that gateway A common mistake is mixing keys: OpenAI key with relay base URL Relay key with OpenAI base URL Old test key from a disabled project Key copied with a leading or trailing space When you see 401 Unauthorized , print the first and last few characters of the key locally and compare it with the dashboard. Do not log the full key. 3. Check the model name from the live list Do not guess model names from memory. Gateway model names can change as upstream availability changes. Before using gpt-5.5 , gpt-5.4 , or a Claude Code model, check the current model list . Copy the model id exactly. resp = client . chat . completions . create ( model = " gpt-5.5 " , messages = [{ " role " : " user " , " content " : " Say hello in one sentence. " }], ) If the model name is wrong, you usually get 404 , model_not_found , or a gateway-specific validation error. 4. Test with the smallest possible request Before debugging your whole app, run one tiny request: resp = client . chat . completions . create ( model = " gpt-5.5 " , messages = [{ " role " : " user " , " content " : " ping " }], max_tokens = 20 , ) print ( resp . choices [ 0 ]. message . content ) If this works, the base URL, key, and model are probably fine. Your bug is likely in the app layer: streaming, tool calling, message format, proxy settings, or retry logi