<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Skapi</title>
    <description>The latest articles on DEV Community by Skapi (@skapi_api).</description>
    <link>https://dev.to/skapi_api</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3161077%2Ff42727c1-df28-406e-85bd-b25b4a006818.png</url>
      <title>DEV Community: Skapi</title>
      <link>https://dev.to/skapi_api</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/skapi_api"/>
    <language>en</language>
    <item>
      <title>The real price of a backend in São Paulo: $529 on AWS vs $19 on Skapi</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Fri, 02 Oct 2026 05:52:52 +0000</pubDate>
      <link>https://dev.to/skapi_api/the-real-price-of-a-backend-in-sao-paulo-529-on-aws-vs-19-on-skapi-l3e</link>
      <guid>https://dev.to/skapi_api/the-real-price-of-a-backend-in-sao-paulo-529-on-aws-vs-19-on-skapi-l3e</guid>
      <description>&lt;h2&gt;
  
  
  The real price of a backend in São Paulo: $529 on AWS vs $19 on Skapi
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://www.skapi.com/articles/2026-09-28-00" rel="noopener noreferrer"&gt;the last article&lt;/a&gt; we said a Skapi project in São Paulo runs at a fraction of what a traditional server backend costs there, and that getting there was hard. Here is the bill, line by line: $528.71 a month for the traditional stack, $19 for the same backend on Skapi, and how we made that work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.broadwayinc.com%2Fimg%2Fskapi-logo-cut-small.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.broadwayinc.com%2Fimg%2Fskapi-logo-cut-small.png" alt="Skapi" width="244" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why São Paulo is expensive
&lt;/h2&gt;

&lt;p&gt;AWS charges more in São Paulo than in most of its regions, and it is not a rounding error. Here is what the same building blocks cost in São Paulo (sa-east-1) and in N. Virginia (us-east-1), the region most tutorials silently assume you are in. List prices, on demand, as of September 2026.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Building block&lt;/th&gt;
&lt;th&gt;São Paulo&lt;/th&gt;
&lt;th&gt;N. Virginia&lt;/th&gt;
&lt;th&gt;São Paulo costs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;App server, t3.medium, per hour&lt;/td&gt;
&lt;td&gt;$0.0672&lt;/td&gt;
&lt;td&gt;$0.0416&lt;/td&gt;
&lt;td&gt;62% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database, RDS db.t3.medium Multi-AZ, per hour&lt;/td&gt;
&lt;td&gt;$0.302&lt;/td&gt;
&lt;td&gt;$0.145&lt;/td&gt;
&lt;td&gt;108% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database storage, gp3, per GB-month&lt;/td&gt;
&lt;td&gt;$0.219&lt;/td&gt;
&lt;td&gt;$0.115&lt;/td&gt;
&lt;td&gt;90% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server disk, EBS gp3, per GB-month&lt;/td&gt;
&lt;td&gt;$0.152&lt;/td&gt;
&lt;td&gt;$0.080&lt;/td&gt;
&lt;td&gt;90% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAT gateway, per hour&lt;/td&gt;
&lt;td&gt;$0.093&lt;/td&gt;
&lt;td&gt;$0.045&lt;/td&gt;
&lt;td&gt;107% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application load balancer, per hour&lt;/td&gt;
&lt;td&gt;$0.034&lt;/td&gt;
&lt;td&gt;$0.0225&lt;/td&gt;
&lt;td&gt;51% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File storage, S3 Standard, per GB-month&lt;/td&gt;
&lt;td&gt;$0.0405&lt;/td&gt;
&lt;td&gt;$0.023&lt;/td&gt;
&lt;td&gt;76% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data transfer out, per GB&lt;/td&gt;
&lt;td&gt;$0.150&lt;/td&gt;
&lt;td&gt;$0.090&lt;/td&gt;
&lt;td&gt;67% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDN delivery, CloudFront, per GB&lt;/td&gt;
&lt;td&gt;$0.110&lt;/td&gt;
&lt;td&gt;$0.085&lt;/td&gt;
&lt;td&gt;29% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDN requests, CloudFront HTTPS, per 10,000&lt;/td&gt;
&lt;td&gt;$0.022&lt;/td&gt;
&lt;td&gt;$0.010&lt;/td&gt;
&lt;td&gt;120% more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email, SES, per 1,000 messages&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;the same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User accounts, Cognito Lite, per monthly active user&lt;/td&gt;
&lt;td&gt;$0.0055&lt;/td&gt;
&lt;td&gt;$0.0055&lt;/td&gt;
&lt;td&gt;the same&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;CDN prices follow the viewer, not the origin, so a Brazilian audience pays the South America rate wherever the files live.&lt;/p&gt;

&lt;p&gt;Two things stand out. The parts that run all month, servers, databases and gateways, carry the steepest premium. The parts that only cost when someone uses them, email and identity, cost the same everywhere. Keep that split in mind. It is the whole story.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill for a traditional backend
&lt;/h2&gt;

&lt;p&gt;Let's price a modest production app. Not a toy: 50,000 registered users, 20,000 of whom sign in each month, 3 GB of database, 120 GB of uploaded files, 100 GB served through the CDN each month, and 5,000 transactional emails. That is the exact ceiling of Skapi's Standard plan, so the comparison at the end is fair.&lt;/p&gt;

&lt;p&gt;Built the traditional way in São Paulo, with the redundancy you would want for something real, the bill looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;Per month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;App servers&lt;/td&gt;
&lt;td&gt;2 × t3.medium, one per availability zone&lt;/td&gt;
&lt;td&gt;$98.11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server disks&lt;/td&gt;
&lt;td&gt;2 × 30 GB gp3, plus daily snapshots&lt;/td&gt;
&lt;td&gt;$13.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load balancer&lt;/td&gt;
&lt;td&gt;Application Load Balancer, one capacity unit&lt;/td&gt;
&lt;td&gt;$32.85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAT gateway&lt;/td&gt;
&lt;td&gt;Outbound access for servers in private subnets&lt;/td&gt;
&lt;td&gt;$69.75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;RDS PostgreSQL db.t3.medium, Multi-AZ, 20 GB&lt;/td&gt;
&lt;td&gt;$229.22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File storage&lt;/td&gt;
&lt;td&gt;S3, 120 GB and its requests&lt;/td&gt;
&lt;td&gt;$6.68&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDN&lt;/td&gt;
&lt;td&gt;CloudFront, 100 GB and 2 million requests&lt;/td&gt;
&lt;td&gt;$15.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API traffic out&lt;/td&gt;
&lt;td&gt;30 GB of responses to the internet&lt;/td&gt;
&lt;td&gt;$4.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;SES, 5,000 messages&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User accounts&lt;/td&gt;
&lt;td&gt;Cognito Lite, 20,000 active users, first 10,000 free&lt;/td&gt;
&lt;td&gt;$55.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS and monitoring&lt;/td&gt;
&lt;td&gt;Route 53 zone, CloudWatch basics&lt;/td&gt;
&lt;td&gt;$3.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$528.71&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can cut that down. Run one server in a public subnet, drop the load balancer and the NAT gateway, run the database in a single availability zone, and it comes to about $256 a month. It also means the site goes down every time you deploy, reboot, or the database enters its maintenance window, and there is nothing to fail over to when the one server dies.&lt;/p&gt;

&lt;p&gt;For scale: the same production stack in N. Virginia comes to about $311. São Paulo adds 70% to the bill for the privilege of being close to your users.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;List prices, on demand, before discounts. A one-year reservation trims the server and database lines by roughly a third, in exchange for paying for capacity you may never use. AWS's always-free allowances would cover the CDN and transfer lines at this size; they are shown at list price because they are the lines that grow with success.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the bill does not show
&lt;/h2&gt;

&lt;p&gt;The monthly figure is the easy part. Every line in it is also a thing you have to build, and then keep.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Servers.&lt;/strong&gt; Choose an image, harden it, open the right ports and only those, install a runtime, write a deploy pipeline, put TLS in front of it, and then keep it patched for the rest of its life. Two servers means doing everything twice and keeping them identical. Autoscaling means an image pipeline on top of that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The database.&lt;/strong&gt; A subnet group, a parameter group, a security group, a backup window, a maintenance window, a failover test you actually run, connection pooling so a traffic spike does not exhaust it, and a plan for the day the disk fills up. Multi-AZ doubles the price, and it is the only version you would put a business on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Files and the CDN.&lt;/strong&gt; A bucket with public access blocked, a policy that lets your servers write and nobody else, presigned URLs so users can upload without going through your servers, CORS rules, lifecycle rules. Then a CloudFront distribution with origin access control, cache policies, invalidations, a custom domain, and a certificate that, for CloudFront, must be issued in N. Virginia whatever region your files live in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The network.&lt;/strong&gt; A VPC, public and private subnets in two zones, route tables, an internet gateway, a NAT gateway, and IAM roles narrow enough to be safe and wide enough to work. None of this serves a single request. It is the price of admission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And then the months after.&lt;/strong&gt; Patches, certificate renewals, disk alarms, the 3 a.m. page, the database upgrade you have been putting off. If one developer spends even four hours a month keeping this running, that time alone costs more than the entire Skapi plan below, at any rate you would pay a developer in São Paulo, Buenos Aires or Bogotá.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated emails, compared
&lt;/h2&gt;

&lt;p&gt;Email is the line that looks free and is not. SES charges $0.10 per thousand messages, so 5,000 emails cost fifty cents in São Paulo, exactly as in Virginia. The cost is everything around that number.&lt;/p&gt;

&lt;p&gt;Before SES will send a real email you verify a domain, publish DKIM, SPF and DMARC records, and apply to leave the sandbox with a written description of how you handle bounces and complaints. Then you build that handling: notification topics, a suppression list, retries. Then the actual emails: a verification flow with a code that expires, a welcome email, an invitation, each one a template, a trigger and a queue so a burst of signups does not hit a rate limit. If you want to send a newsletter, add subscriber storage, a subscribe and unsubscribe flow that complies with the law in every country you send to, and a sending pipeline that paces itself.&lt;/p&gt;

&lt;p&gt;On Skapi that whole list is a settings page. Verification, welcome and invitation emails go out from your project's own sending address with no code. Newsletters have subscribers, subscription and unsubscribe links, and a send button. Standard includes 5,000 sends a month and Premium 50,000, and the deliverability plumbing is done once, for everyone, and kept warm.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same backend on Skapi
&lt;/h2&gt;

&lt;p&gt;Same users, same data, same traffic, same city:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q2j9u5n5xmwwfe1s81a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q2j9u5n5xmwwfe1s81a.png" alt="Backend with Skapi" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;28 times less than the production stack. 13 times less than the one that goes down when you deploy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over a year, that is $6,345 against $228. The difference is not a discount. It is a different way of running a backend.&lt;/p&gt;

&lt;p&gt;Here is what each side gives you for the money, one line at a time. First the cheapest of each: the AWS bare minimum next to Skapi Standard.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;AWS, bare minimum&lt;/th&gt;
&lt;th&gt;Skapi Standard&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price per month&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$255.85&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$19&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;RDS PostgreSQL with 2 vCPU and 4 GB of memory, one zone, 20 GB disk: $114.61&lt;/td&gt;
&lt;td&gt;3 GB, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File storage&lt;/td&gt;
&lt;td&gt;S3, 120 GB: $6.68&lt;/td&gt;
&lt;td&gt;120 GB, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web hosting&lt;/td&gt;
&lt;td&gt;Served from your one server, or from a bucket you configure&lt;/td&gt;
&lt;td&gt;Included, with a subdomain and HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDN and bandwidth&lt;/td&gt;
&lt;td&gt;CloudFront 100 GB, 2 million requests, 30 GB of API traffic: $19.90&lt;/td&gt;
&lt;td&gt;100 GB, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User accounts&lt;/td&gt;
&lt;td&gt;Cognito Lite, 20,000 active users: $55.00&lt;/td&gt;
&lt;td&gt;50,000 accounts, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenID login&lt;/td&gt;
&lt;td&gt;Set up by you in Cognito&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automated emails&lt;/td&gt;
&lt;td&gt;SES, 5,000 messages: $0.50. Templates built by you&lt;/td&gt;
&lt;td&gt;5,000 sends a month, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Newsletters&lt;/td&gt;
&lt;td&gt;Built by you: list, opt-out link, sending&lt;/td&gt;
&lt;td&gt;5,000 subscribers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Realtime data&lt;/td&gt;
&lt;td&gt;Built by you, on the one server&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Push notifications&lt;/td&gt;
&lt;td&gt;Built by you&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhooks&lt;/td&gt;
&lt;td&gt;Endpoints you write and host&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API secret keys&lt;/td&gt;
&lt;td&gt;Kept on your server&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App server&lt;/td&gt;
&lt;td&gt;1 × t3.medium, 30 GB disk with snapshots: $55.66&lt;/td&gt;
&lt;td&gt;None to run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring and DNS&lt;/td&gt;
&lt;td&gt;CloudWatch and Route 53: $3.50&lt;/td&gt;
&lt;td&gt;Usage meters in the dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redundant&lt;/td&gt;
&lt;td&gt;No: one server, one zone&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend deploys&lt;/td&gt;
&lt;td&gt;The site goes down each time&lt;/td&gt;
&lt;td&gt;None needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;A bigger server, by hand&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;A day&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upkeep&lt;/td&gt;
&lt;td&gt;Yours&lt;/td&gt;
&lt;td&gt;Ours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And the full version of each: the AWS production stack next to Skapi Premium. To be generous to AWS, we added database overage to Premium until it matches the 20 GB database disk of the AWS stack, even though Premium already outdoes that stack everywhere else: five times the files, four times the bandwidth, ten times the emails.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;AWS, production&lt;/th&gt;
&lt;th&gt;Skapi Premium&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price per month&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$528.71&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$94&lt;/strong&gt; ($89 plan, $5 overage)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;RDS PostgreSQL with 2 vCPU and 4 GB of memory, two zones, 20 GB disk: $229.22&lt;/td&gt;
&lt;td&gt;20 GB: 10 GB included, 10 GB more at $0.50 per GB, $5.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File storage&lt;/td&gt;
&lt;td&gt;S3, 120 GB: $6.68&lt;/td&gt;
&lt;td&gt;600 GB, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web hosting&lt;/td&gt;
&lt;td&gt;Served from your servers, or from a bucket you configure&lt;/td&gt;
&lt;td&gt;Included, with a subdomain and HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDN and bandwidth&lt;/td&gt;
&lt;td&gt;CloudFront 100 GB, 2 million requests, 30 GB of API traffic: $19.90&lt;/td&gt;
&lt;td&gt;400 GB, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User accounts&lt;/td&gt;
&lt;td&gt;Cognito Lite, 20,000 active users: $55.00&lt;/td&gt;
&lt;td&gt;100,000 accounts, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenID login&lt;/td&gt;
&lt;td&gt;Set up by you in Cognito&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automated emails&lt;/td&gt;
&lt;td&gt;SES, 5,000 messages: $0.50. Templates built by you&lt;/td&gt;
&lt;td&gt;50,000 sends a month, included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Newsletters&lt;/td&gt;
&lt;td&gt;Built by you: list, opt-out link, sending&lt;/td&gt;
&lt;td&gt;50,000 subscribers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Realtime data&lt;/td&gt;
&lt;td&gt;Built by you, across two servers&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Push notifications&lt;/td&gt;
&lt;td&gt;Built by you&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhooks&lt;/td&gt;
&lt;td&gt;Endpoints you write and host&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API secret keys&lt;/td&gt;
&lt;td&gt;Kept on your servers&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App servers&lt;/td&gt;
&lt;td&gt;2 × t3.medium, 2 × 30 GB disk with snapshots: $111.31&lt;/td&gt;
&lt;td&gt;None to run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load balancer&lt;/td&gt;
&lt;td&gt;One capacity unit: $32.85&lt;/td&gt;
&lt;td&gt;None to run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAT gateway&lt;/td&gt;
&lt;td&gt;For the private subnets: $69.75&lt;/td&gt;
&lt;td&gt;None to run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring and DNS&lt;/td&gt;
&lt;td&gt;CloudWatch and Route 53: $3.50&lt;/td&gt;
&lt;td&gt;Usage meters in the dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redundant&lt;/td&gt;
&lt;td&gt;Yes: two zones, set up and tested by you&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend deploys&lt;/td&gt;
&lt;td&gt;Rolling, through a pipeline you build&lt;/td&gt;
&lt;td&gt;None needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Rules you write and tune&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upkeep&lt;/td&gt;
&lt;td&gt;Yours&lt;/td&gt;
&lt;td&gt;Ours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is $94 against $528.71. Even with the overage counted, Premium costs more than 5 times less.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The prices in each AWS column add up to the total at the top of it. The lines without a price are the ones the bill leaves out: they are paid in time instead. Both AWS columns are the bills from earlier in this article, sized for 120 GB of files and 20,000 active users. Sized for what Premium includes (600 GB of files, 400 GB of bandwidth, 50,000 emails, 40,000 active users) on the same servers, the AWS production bill comes to about $728 a month.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Standard is $19 a month in São Paulo, the same as in Oregon, Paris, Mumbai, Singapore or Seoul. For that you get the database, file storage, CDN, web hosting, user accounts with OpenID login, automated emails and newsletters, realtime data, API secret keys and webhooks, with room for 50,000 user accounts, 3 GB of database, 120 GB of files and 100 GB of bandwidth. Premium is $89 for 100,000 accounts, 10 GB of database, 600 GB of files and 400 GB of bandwidth. A small project runs on Free for nothing at all.&lt;/p&gt;

&lt;p&gt;And all of that is before counting a single hour of anyone's time. The four hours a month from the section above cost more than the Skapi plan on their own.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we got the price down
&lt;/h2&gt;

&lt;p&gt;We are not going to draw you our architecture, and we do not have secret hardware. What we have is a set of rules we did not break, and in São Paulo they matter more than anywhere else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nothing idles.&lt;/strong&gt; Look back at the first table. The steep premiums all sit on things billed by the hour whether or not anyone is using them. A traditional backend in São Paulo pays for 730 hours of servers, 730 hours of database and 730 hours of gateway every month, at 3 a.m. on a Sunday as much as at noon on payday. Every part of Skapi is metered by use: a request, a record read, a gigabyte stored, a message sent. When your users are asleep, so is your bill. That is what serverless means to us, and we applied it to every resource in the chain, not just the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed costs are shared, not repeated.&lt;/strong&gt; The expensive lines above, the redundant database, the load balancer, the gateway, are fixed costs a single project pays alone. In Skapi those costs exist once per region, and every project in that region shares them. Your project's share of a fixed cost is close to zero, and that, more than any clever trick, is where the fraction comes from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bytes go to the cheapest place that can hold them.&lt;/strong&gt; Records live in a database that charges for what you store and read, not for a machine that waits. Anything large, files, uploads, hosted sites, record payloads over 32 KB, goes straight to object storage and is served from the CDN, so a download never touches a compute layer at all. Database storage is the most expensive byte in São Paulo, so we keep as few of them there as we can, and &lt;a href="https://dev.to/articles/2026-09-16"&gt;we made that change on every plan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identity and email are services, not servers.&lt;/strong&gt; User accounts live in a managed directory built for that job, with verification, password reset and multi-factor flows already there. Email is a queue, with delivery, bounces, complaints and sender reputation handled once for the whole platform. These are the parts that cost the same in every region, and they are the parts nobody should rebuild per project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scale is a consequence, not a plan.&lt;/strong&gt; Because every part is metered, a spike in traffic is a larger bill for that hour, not an outage, and a quiet month is a smaller one. There is no instance size to pick, no capacity to reserve, no failover to rehearse. The pieces that would go down are the pieces we do not run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security comes with the shape.&lt;/strong&gt; There are no servers in your project to patch, no SSH, no open ports. Every request is authenticated and authorized on its own, every component talks to the next with the narrowest permission that works, data is encrypted in transit and at rest, and private records can be &lt;a href="https://dev.to/articles/2026-09-07-02"&gt;encrypted in the browser&lt;/a&gt; under your user's own password. A project's data never leaves the region it was created in. For Latin American teams, that last line often matters as much as the price.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why São Paulo was hard
&lt;/h2&gt;

&lt;p&gt;Here is the part we promised to explain. The rules above are how Skapi works everywhere. São Paulo tested every one of them.&lt;/p&gt;

&lt;p&gt;Serverless does not escape the regional premium. It meets it on a different bill. Every meter we rely on runs faster in São Paulo: storage per gigabyte, requests per million, bytes out per gigabyte. Some managed services cost more there, some arrived later than in other regions, and a few of the shared pieces that make a fixed cost disappear had to be planned again so they would still disappear at São Paulo prices. Keeping the plan price flat, $19 in São Paulo exactly as in Oregon, meant wasting nothing: fewer hops per request, caching wherever it pays for itself, moving bytes as little as possible, and checking that no component, anywhere in the chain, sits idle on your behalf.&lt;/p&gt;

&lt;p&gt;It was the hardest region we have opened, and we opened it on purpose. We want developers in Brazil and across Latin America to have affordable, efficient, solid access to backend infrastructure in their own region, at the same price as everyone else, without the tax the region has quietly charged for years. A student in Recife and a startup in Santiago should be able to run something real for the price of a few coffees a month. Now they can.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;Create a project, choose São Paulo, and run the numbers against your own AWS bill. If the difference is not obvious in the first month, we would like to hear about it.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Olá, São Paulo: Skapi now runs in São Paulo, its first South American region</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Wed, 30 Sep 2026 10:30:40 +0000</pubDate>
      <link>https://dev.to/skapi_api/ola-sao-paulo-skapi-now-runs-in-sao-paulo-its-first-south-american-region-42g7</link>
      <guid>https://dev.to/skapi_api/ola-sao-paulo-skapi-now-runs-in-sao-paulo-its-first-south-american-region-42g7</guid>
      <description>&lt;h2&gt;
  
  
  Olá, São Paulo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.broadwayinc.com%2Fimg%2FFlag_Brazil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.broadwayinc.com%2Fimg%2FFlag_Brazil.png" alt="Flag of Brazil" width="500" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Skapi now runs in Brazil. São Paulo is our newest region, and it is the first place in South America where a project's database, its files and its user accounts all live on the same continent as the people using them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.broadwayinc.com%2Fimg%2Fskapi-logo-cut-small.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.broadwayinc.com%2Fimg%2Fskapi-logo-cut-small.png" alt="Skapi" width="244" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Olá, Brasil! Olá, América Latina! A partir de hoje, a Skapi roda em São Paulo. Se você cria aplicações para usuários no Brasil ou em qualquer canto da América Latina, eles não precisam mais cruzar meio mundo a cada requisição: o banco de dados, os arquivos e as contas do seu projeto ficam pertinho, e cada login, cada consulta e cada upload responde bem mais rápido. Um abraço, e bom código!&lt;/p&gt;

&lt;h2&gt;
  
  
  The first South American region
&lt;/h2&gt;

&lt;p&gt;Until now, the closest Skapi region to a user in Rio de Janeiro was in Paris, 9,167 km away on the other side of the Atlantic. A user in Lima was sent to Oregon, 7,720 km to the north. Every login, every query, every file upload made that journey and came back. It worked, and it worked well, but physics always took its cut.&lt;/p&gt;

&lt;p&gt;São Paulo changes the shape of that map. Here is what the same requests look like now:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your user is in&lt;/th&gt;
&lt;th&gt;To São Paulo&lt;/th&gt;
&lt;th&gt;Nearest region before&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rio de Janeiro&lt;/td&gt;
&lt;td&gt;361 km&lt;/td&gt;
&lt;td&gt;9,167 km (Paris)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Belo Horizonte&lt;/td&gt;
&lt;td&gt;491 km&lt;/td&gt;
&lt;td&gt;8,913 km (Paris)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Porto Alegre&lt;/td&gt;
&lt;td&gt;852 km&lt;/td&gt;
&lt;td&gt;10,254 km (Paris)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brasília&lt;/td&gt;
&lt;td&gt;872 km&lt;/td&gt;
&lt;td&gt;8,725 km (Paris)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buenos Aires&lt;/td&gt;
&lt;td&gt;1,675 km&lt;/td&gt;
&lt;td&gt;10,852 km (Oregon)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Santiago&lt;/td&gt;
&lt;td&gt;2,585 km&lt;/td&gt;
&lt;td&gt;10,099 km (Oregon)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lima&lt;/td&gt;
&lt;td&gt;3,454 km&lt;/td&gt;
&lt;td&gt;7,720 km (Oregon)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bogotá&lt;/td&gt;
&lt;td&gt;4,328 km&lt;/td&gt;
&lt;td&gt;6,339 km (Oregon)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A user in Rio de Janeiro is now twenty-five times closer to their data. A user in Buenos Aires, six times closer. That difference is not a benchmark you have to squint at. It is the feeling of a page that loads before the spinner has a reason to appear, a form that saves the instant it is submitted, and a file that starts downloading rather than thinking about it first.&lt;/p&gt;

&lt;p&gt;From Colombia and Venezuela south, São Paulo is now the nearest region by a wide margin. Users in Mexico and most of Central America are still closest to Oregon, and Skapi keeps offering Oregon first there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Skapi runs
&lt;/h2&gt;

&lt;p&gt;Six regions across North America, South America, Europe and Asia, placed so that most of the connected world has a Skapi region within a short hop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flhd1rbicahq7wp48psg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flhd1rbicahq7wp48psg1.png" alt="Skapi covers 6 regions including LATAM" width="800" height="319"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Every dot is a full Skapi region running the complete stack.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Region&lt;/th&gt;
&lt;th&gt;Where it is&lt;/th&gt;
&lt;th&gt;Project ID starts with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;us-west-2&lt;/td&gt;
&lt;td&gt;Oregon, United States&lt;/td&gt;
&lt;td&gt;&lt;code&gt;us72&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sa-east-1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;São Paulo, Brazil&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sa31&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eu-west-3&lt;/td&gt;
&lt;td&gt;Paris, France&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eu73&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ap-south-1&lt;/td&gt;
&lt;td&gt;Mumbai, India&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ap51&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ap-southeast-1&lt;/td&gt;
&lt;td&gt;Singapore&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ap41&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ap-northeast-2&lt;/td&gt;
&lt;td&gt;Seoul, South Korea&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ap22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read that list from west to east and the spacing is deliberate. Oregon anchors North America, and São Paulo now does the same for South America. Paris covers Europe, North Africa and the Middle East. Mumbai, Singapore and Seoul carry the largest concentration of internet users on the planet between them.&lt;/p&gt;

&lt;p&gt;Every one of those regions runs the same Skapi. The same SDK call, the same API, the same authentication, the same record model. There is no South American edition with fewer features and no waiting for a region to catch up. A region is either serving your project completely or it is not serving it at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now you choose where your project lives
&lt;/h2&gt;

&lt;p&gt;When you create a project, you pick the region.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fcdn.broadwayinc.com%2Farticles%2Fimg%2Fcreate-project-region.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fcdn.broadwayinc.com%2Farticles%2Fimg%2Fcreate-project-region.webp" alt="The Create a new project dialog: a project name, an optional description, and a Region field reading Sao Paulo, Brazil (closest to you), with a note that the project's data is stored in the region you pick and cannot be moved later" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The new-project dialog. The closest region is offered first, every region is in the list, and the note under it says the one thing to know before you continue.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Skapi offers you the closest one first, worked out from where you are, so the fast choice is also the default choice. But the list is right there, and the right answer is often not your own city. If you are a studio in Lisbon with an audience in Rio, put the project in São Paulo. If you are three founders in São Paulo selling to Madrid, put it in Paris. Your users are the ones making the round trip thousands of times a day. You make it twice.&lt;/p&gt;

&lt;p&gt;Pick deliberately, because a project stays where you put it. The region is baked into the first four characters of the project ID, which is why a São Paulo project announces itself as &lt;code&gt;sa31...&lt;/code&gt; for the rest of its life. It is a permanent decision in the same way a good foundation is permanent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data that stays where you put it
&lt;/h2&gt;

&lt;p&gt;There is a second reason to care about a region. A project created in São Paulo keeps its database records, its uploaded files, its hosted site and its user accounts in Brazil. Not routed through it. Stored in it.&lt;/p&gt;

&lt;p&gt;For teams whose customers ask where their data is kept, and for anyone working under the LGPD, Brazil's data protection law, that single sentence is usually the one that unblocks the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why São Paulo means so much to us
&lt;/h2&gt;

&lt;p&gt;A new region is always satisfying to open, but this one means more to us than most, for two reasons.&lt;/p&gt;

&lt;p&gt;The first is reach. South America is home to hundreds of millions of people who live on their phones, and until now every one of them was a long way from any Skapi project. That distance showed up in your business as slower pages, abandoned forms and users who quietly gave up. With São Paulo, your product can reach them properly. It loads faster, it saves faster, and it feels like it was built for them, because now it runs right next to them.&lt;/p&gt;

&lt;p&gt;The second is cost. Running a traditional server backend on AWS in South America is expensive, noticeably more so than the same servers in North America or Europe, and that cost alone has kept many teams from serving the region well, or at all. We made sure a Skapi project in São Paulo runs at a fraction of that price. Making that possible was a real challenge for us, and it took a lot more than flipping a switch. That story deserves its own space, so we will cover it in the next article.&lt;/p&gt;

&lt;p&gt;So: create a project, choose São Paulo, and let your Brazilian and Latin American users feel what their applications were capable of all along.&lt;/p&gt;

&lt;p&gt;Bem-vindos a bordo. Bom desenvolvimento, e até breve.&lt;/p&gt;

</description>
      <category>security</category>
      <category>database</category>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Skapi 2.0 Is Out: Private Data Encryption, API Streaming, and Safer Backend Requests</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Mon, 07 Sep 2026 09:54:02 +0000</pubDate>
      <link>https://dev.to/skapi_api/skapi-20-is-out-private-data-encryption-api-streaming-and-safer-backend-requests-156b</link>
      <guid>https://dev.to/skapi_api/skapi-20-is-out-private-data-encryption-api-streaming-and-safer-backend-requests-156b</guid>
      <description>&lt;h2&gt;
  
  
  Skapi 2.0 is out
&lt;/h2&gt;

&lt;p&gt;The stable tag has moved: &lt;code&gt;npm i skapi-js&lt;/code&gt; installs 2.0 today. Private records can now be encrypted in the browser, under your user's own password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.bunnyquery.com%2Fskapi-square-128.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.bunnyquery.com%2Fskapi-square-128.webp" alt="Skapi" width="128" height="128"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i skapi-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In August that command still installed 1.8.3, and we said nothing you were running would change until you asked for it. This is you asking for it. If you are not ready, &lt;code&gt;npm i skapi-js@1.8.3&lt;/code&gt; keeps you exactly where you are.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bunnyquery.com/articles/2026-08-12" rel="noopener noreferrer"&gt;The release candidate post&lt;/a&gt; covered the naming, the polling queues and the session storage work. This one is about what landed on top of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Private records can be encrypted in the browser now
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;new Skapi("&amp;lt;Project ID&amp;gt;", { encryption: true })&lt;/code&gt; encrypts the &lt;code&gt;data&lt;/code&gt; of every record you write to &lt;code&gt;access_group: 'private'&lt;/code&gt;, and the contents of the files attached to it, before any of it leaves the page. The key is derived from the user's own password and never leaves their device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Skapi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;Project ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;encryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;minPasswordLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;diagnosis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confidential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The calls do not change. &lt;code&gt;postRecord()&lt;/code&gt; seals, &lt;code&gt;getRecords()&lt;/code&gt; opens, and there is no key for your end user to manage. Sharing is the call it always was: &lt;code&gt;grantPrivateRecordAccess()&lt;/code&gt; wraps that record's data key to the recipient's published public key, so the owner does not have to be online when the grantee reads, and &lt;code&gt;removePrivateRecordAccess()&lt;/code&gt; rolls the data key, which makes revocation forward only.&lt;/p&gt;

&lt;p&gt;Skapi has always decided who may read a record, on the server, against a signed identity the browser cannot forge. What is new is that the bytes stop being readable by the people running the database. In the words our documentation uses: a provider who dumps the database, the storage bucket and every request log cannot recover that record's &lt;code&gt;data&lt;/code&gt; without a successful offline guess against the user's password.&lt;/p&gt;

&lt;h2&gt;
  
  
  An API response can render while it is still being written
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;clientSecretRequest()&lt;/code&gt; now takes &lt;code&gt;stream: true&lt;/code&gt; and &lt;code&gt;onStream(chunk, seq, via)&lt;/code&gt;. The server reads the destination's response incrementally and relays the raw bytes as they arrive, so your page renders an answer while the destination is still writing it. Add &lt;code&gt;realtime: true&lt;/code&gt; and each piece also comes over skapi's websocket the moment it is relayed, instead of on the next poll tick.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clientSecretRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;clientSecretName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.openai.com/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer $CLIENT_SECRET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// the destination's own flag&lt;/span&gt;
    &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// skapi's flag: relay the answer as it arrives&lt;/span&gt;
    &lt;span class="na"&gt;realtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onStream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;via&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;readEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your key still never reaches the browser: &lt;code&gt;$CLIENT_SECRET&lt;/code&gt; is filled in on the server as the call goes out. Skapi parses nothing, so the framing and the buffering of a chunk that ended mid frame are yours, which is why this works against any destination on day one rather than only the ones we know about. The websocket is an accelerator and never the source of truth: a dropped socket is not an error, and the read continues at poll speed.&lt;/p&gt;

&lt;p&gt;One trap. &lt;code&gt;stream&lt;/code&gt; says how skapi reads the response; asking the destination to produce one incrementally is a separate flag in your own request body. Skapi inspects neither, so setting only one of the two goes wrong quietly, with no error from either side.&lt;/p&gt;

&lt;h2&gt;
  
  
  forwardRequest is the method we promised and never named
&lt;/h2&gt;

&lt;p&gt;The release candidate post said 2.0 had one genuinely new method, then no section ever said which. It is &lt;code&gt;forwardRequest()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forwardRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;How did last quarter go?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.yourbackend.com/chat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onStream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It relays a request to your own backend from skapi's servers and streams the response back, with your project's API key attached server side where the browser cannot read it. Your backend also receives &lt;code&gt;x-skapi-user&lt;/code&gt;, taken from the verified session, and a request that tries to set an &lt;code&gt;x-skapi-&lt;/code&gt; header itself is rejected, so that attribution is safe to trust. The first argument can be a submit event, a form element, a &lt;code&gt;FormData&lt;/code&gt; or a plain object, and a form goes as &lt;code&gt;multipart/form-data&lt;/code&gt; with its files.&lt;/p&gt;

&lt;p&gt;Your backend has to resolve to a public address, so private ranges, loopback and cloud metadata addresses are refused. The destination instructions ride in a header capped at 4096 characters measured after escaping, where each non-ASCII character costs six, so put anything long in the body. And there is no re-attach call for this one: the response is yours once, as it arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things to know before you upgrade
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Your &lt;code&gt;data&lt;/code&gt; is stored as JSON text, and the SDK is the only decoder.&lt;/strong&gt; The API encodes it on every write regardless of which client wrote it, so an older SDK reading one of these records gets the raw encoded string instead of your object, quietly, with no error. The test is one line: a &lt;code&gt;data&lt;/code&gt; you expected as an object that reads back as a string beginning &lt;code&gt;!J%&lt;/code&gt; is this. If a project has more than one client on it, a mobile build, a Node worker, a REST caller, upgrade them together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two changes ship with the API rather than the SDK, so they are already true for you.&lt;/strong&gt; Moving a record into or out of &lt;code&gt;'private'&lt;/code&gt; clears every private access grant on it, encryption or not, so grant those users again after the move. And only a record's owner may cross that line: a master account still reads, edits and deletes anything in its project, but it can no longer move somebody else's record between &lt;code&gt;'private'&lt;/code&gt; and a numbered group, because with encryption in play that would destroy the record while reporting success.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Four small changes arrive whether you ask or not.&lt;/strong&gt; &lt;code&gt;skapi.util.decodeServiceId()&lt;/code&gt; is &lt;code&gt;decodeProjectId()&lt;/code&gt;, with no alias. &lt;code&gt;table: 'name'&lt;/code&gt; on an update no longer moves that record to the public access group, so pass &lt;code&gt;table: { name: 'name', access_group: 0 }&lt;/code&gt; if you wanted the move. &lt;code&gt;deleteRecords()&lt;/code&gt; returns normalized records, so read &lt;code&gt;record_id&lt;/code&gt; and &lt;code&gt;table.name&lt;/code&gt; rather than the old short keys. And &lt;code&gt;data: null&lt;/code&gt; stores a null instead of removing the field.&lt;/p&gt;

&lt;p&gt;Everything else waits to be asked for, including the new &lt;code&gt;require_login&lt;/code&gt; project setting, read from &lt;code&gt;getConnectionInfo()&lt;/code&gt; as &lt;code&gt;conf.require_login&lt;/code&gt;. It makes the SDK refuse record reads from a signed out session: a guard rail against a page leaking your public records by accident, not an access control, since the API still serves &lt;code&gt;access_group: 0&lt;/code&gt; to anyone who asks.&lt;/p&gt;

&lt;p&gt;One correction while you have &lt;a href="https://docs.skapi.com/versionlog/versions.html" rel="noopener noreferrer"&gt;the changelog&lt;/a&gt; open. &lt;code&gt;condition: '&amp;lt;='&lt;/code&gt; on a string index value as an "ends with" search is a 1.8.0 change, not a 2.0 one, and the API serves it, so it already works from whatever version you are running today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bugs we could not have found on our own
&lt;/h2&gt;

&lt;p&gt;None of the fixes in that data section came from reading our own code. A string containing &lt;code&gt;*add&lt;/code&gt; destroying a record is not something a test suite finds: somebody has to be storing a sentence a real person wrote, in a real app, before the storage format ever gets the chance to misread it.&lt;/p&gt;

&lt;p&gt;So thank you to everyone who put a project that mattered to them on a release candidate, kept &lt;code&gt;skapi-js@rc&lt;/code&gt; installed while it settled, and wrote in with the boring exact detail: the record id, the payload, the call that came back wrong. That is the part of 2.0 we could not have written ourselves.&lt;/p&gt;

&lt;p&gt;Encryption is the first place where Skapi holds something it genuinely cannot read, and widening that is where the next stretch goes. &lt;code&gt;data&lt;/code&gt; is sealed and &lt;code&gt;index.value&lt;/code&gt; is not, because a range read over ordered keys is what keeps a schemaless query cheap. Protecting what you query on without losing the ability to query it is a hard question, and we are not going to answer it badly.&lt;/p&gt;

&lt;p&gt;There is one more thing your backend does not have now. A copy of your users' data it can read.&lt;/p&gt;

&lt;p&gt;Go build with Skapi, and let your users keep their own keys.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>The Fastest Way to Deploy a Site in 2026</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Thu, 11 Dec 2025 08:01:39 +0000</pubDate>
      <link>https://dev.to/skapi_api/the-fastest-way-to-deploy-a-site-in-2026-a10</link>
      <guid>https://dev.to/skapi_api/the-fastest-way-to-deploy-a-site-in-2026-a10</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Most developers don’t need a backend. They need a fast way to launch.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By the end of 2025, the question many developers ask is no longer how to design a website or structure a backend. The question has become much simpler and far more urgent: &lt;strong&gt;What is the fastest way to deploy a site?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Time to launch has become a key competitive factor for indie developers, small teams, and even established engineers who want to move faster without getting caught in configuration loops and infrastructure overhead.&lt;/p&gt;

&lt;p&gt;This article explores that question through the lens of Alex Lee, an early-career developer who has already grown tired of unnecessary complexity, juggling tools, and backend setup. His experience reveals a shift happening in the industry, and why platforms like &lt;a href="//www.skapi.com"&gt;Skapi&lt;/a&gt; are quickly becoming the fastest and most reliable way to deploy a site with zero configuration and no backend work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Development Pain Point Alex Can’t Ignore:
&lt;/h2&gt;

&lt;p&gt;Alex Lee, a developer with two years of experience and a full-time job, decided to work on side projects he genuinely enjoyed. He was excited to get started, but quickly ran into problems he never expected.&lt;/p&gt;

&lt;p&gt;His idea was clear, the frontend code was ready, and the user flow was straightforward. He wasn’t building a large-scale SaaS platform or a complex distributed application. He just wanted to deploy a clean, static site – a ticket pre-sale platform for his favorite music band – with a few simple functional components: email handling, a form submission endpoint, and some basic state storage.&lt;/p&gt;

&lt;p&gt;Sounds simple, right? Looks even simpler:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih73wk2wrd9e57qkpqu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih73wk2wrd9e57qkpqu8.png" alt="music group pre-sale platform website" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yet deploying that site turned into a cycle of dashboards, configs, environment variables, SSL settings, file structures, and DNS delays. What should have taken minutes routinely took hours. For an early-career developer, the complexity felt disproportionate to the goal.&lt;/p&gt;

&lt;p&gt;This frustration is increasingly common, and it reveals the core of a major trend: &lt;strong&gt;developers want the fastest possible way to deploy a site without touching backend infrastructure at all&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Search for the Fastest Way to Deploy a Site
&lt;/h2&gt;

&lt;p&gt;In exploring options, Alex tried several of the popular hosting and deployment tools that have defined the industry over the past few years. Each one promised simplicity. Each one advertised speed. Yet most of them came with underlying complexity that slowed down the process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build configurations&lt;/li&gt;
&lt;li&gt;routing files&lt;/li&gt;
&lt;li&gt;backend functions that required separation&lt;/li&gt;
&lt;li&gt;authentication that needed setup&lt;/li&gt;
&lt;li&gt;email APIs requiring external providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Too much for a developer whose project did not require this level of machinery.&lt;/p&gt;

&lt;p&gt;Alex realized the actual question was not &lt;em&gt;What hosting tool should I use?&lt;/em&gt; but rather:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does deploying a small, functional site still require thinking about backend infrastructure at all?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And this is where Alex found &lt;strong&gt;Skapi&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A New Approach: Zero-Backend Deployment
&lt;/h2&gt;

&lt;p&gt;Skapi stood out immediately because it answered the one question that mattered:&lt;br&gt;
&lt;strong&gt;How fast can a developer deploy a site without touching backend configuration?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alex discovered that Skapi’s architecture does something different from traditional hosting platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It hosts static sites instantly through a simple file-drop interface. index.html becomes your root entry point. Files are served instantly through global CDN.&lt;/li&gt;
&lt;li&gt;It removes the backend setup entirely.&lt;/li&gt;
&lt;li&gt;It provides automatic email handling and newsletter flows.&lt;/li&gt;
&lt;li&gt;It supports authentication without configuration.&lt;/li&gt;
&lt;li&gt;It includes built-in storage and database-like record handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77dcx6m1dqjfx5smhjlq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77dcx6m1dqjfx5smhjlq.jpg" alt="easy no-setup backend" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In other words, Skapi collapses multiple layers of development work into a single environment that requires no manual setup. For a developer trying to deploy a side-project, it represented exactly what the industry has been moving toward: &lt;strong&gt;the fastest way to deploy a site with AI-assisted backend logic and zero configuration.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Skapi let me deploy my project without touching any infrastructure at all.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deploying the Side-Project: A Faster Workflow
&lt;/h2&gt;

&lt;p&gt;Alex’s deployment process shifted dramatically once he tried Skapi. &lt;/p&gt;

&lt;p&gt;The steps became straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prepare the frontend folder.&lt;/li&gt;
&lt;li&gt;Drag files into Skapi’s File Hosting.&lt;/li&gt;
&lt;li&gt;The site is live instantly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwk8g325injgm7xqwi0nm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwk8g325injgm7xqwi0nm.jpg" alt="instant web hosting in 2 minutes" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There were no DNS tasks, no build steps, and no backend endpoints to configure. Alex was able to add a form submission flow with Skapi’s built-in API handling. He integrated email automation without any third-party providers. He added basic data storage without provisioning a database.&lt;/p&gt;

&lt;p&gt;This workflow matched exactly what many developers search for. When someone types &lt;strong&gt;the fastest way to deploy a site&lt;/strong&gt; or &lt;strong&gt;how to deploy a website quickly&lt;/strong&gt;, they’re not looking for server diagrams or configuration tutorials. They want a process that lets them build and deploy without obstacles.&lt;/p&gt;

&lt;p&gt;Skapi delivered that experience by treating backend complexity as optional rather than required.&lt;/p&gt;

&lt;p&gt;The industry is changing. Backends will always matter for large-scale systems, but most modern projects: side-project apps, landing pages, prototypes, microtools, and personal products do not require full backend environments. &lt;/p&gt;

&lt;p&gt;This gap between actual requirements and traditional infrastructure has opened the door for tools like Skapi to reshape deployment workflows entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy Alex’s Conclusion
&lt;/h2&gt;

&lt;p&gt;Alex’s search for the fastest way to deploy a site ended not with a new tool to configure, but with a different approach to development itself. Skapi provided a clear path where the friction of backend setup disappeared, leaving space for building, refining, and launching a real side-project in a fraction of the time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fastest way to deploy a site in 2026 is to use tools that eliminate backend work entirely.&lt;/strong&gt; For anyone searching for the quickest and most effective deployment workflow, the answer is becoming clearer each year: choose the tools that let you build without barriers. &lt;/p&gt;

&lt;p&gt;In that landscape, Skapi is quickly emerging as one of the strongest options.&lt;/p&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>automation</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Vibe coding ultimate beginner tutorial 2025</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Fri, 28 Nov 2025 09:03:07 +0000</pubDate>
      <link>https://dev.to/skapi_api/vibe-coding-ultimate-beginner-tutorial-2025-461o</link>
      <guid>https://dev.to/skapi_api/vibe-coding-ultimate-beginner-tutorial-2025-461o</guid>
      <description>&lt;p&gt;We’ve binged a ton of YouTube videos about vibe coding so you don’t have to. Here’s the basic workflow most vibe coders use, but if you don’t care about all the theory and just want an easy way to get a beautiful, working design, keep reading. We’ll give you an ultimate guide using only two tools to make your project good, fast, and free.&lt;/p&gt;

&lt;p&gt;This is what tech bloggers usually do to build a “vibe-coded” website:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hunt for references on Pinterest.&lt;/li&gt;
&lt;li&gt;Ask an AI to generate unique images based on those references.&lt;/li&gt;
&lt;li&gt;If they want extra flair, they add animations and interactivity in AI tools.&lt;/li&gt;
&lt;li&gt;In a website builder, they describe the idea and build it step by step. Right now, Lovable and Bolt are the big names. If you already know some coding and don’t mind paying a subscription, a lot of people go with Claude Code.&lt;/li&gt;
&lt;li&gt;Build backend logic and webhooks without traditional programming. n8n is the trendy choice for that right now.&lt;/li&gt;
&lt;li&gt;Connect the website to a custom domain and publish it using a hosting service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sounds like a lot, right? Not very “vibe.” And imagine how much you end up paying for all those tools and subscriptions.&lt;/p&gt;

&lt;p&gt;We’re going to save your time and your wallet, and show you how to do it in a short, fast, ultimate way using only AI and one smart tool (Skapi - as you’ve probably guessed already).&lt;/p&gt;

&lt;p&gt;Our project concept is an online pre-sale platform for a concert, where people can sign up and join a waiting list to be the first to know when tickets go on sale, and get the earliest chance to buy them.&lt;/p&gt;

&lt;p&gt;Even though we’re an IT company and know what we’re doing, for the sake of the experiment this project was built by our marketing manager, not a developer. If a marketing girl can do it, you can absolutely do it too.&lt;br&gt;
So if you’re ready - let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for references
&lt;/h2&gt;

&lt;p&gt;First things first - we want a pretty, stylish website. But let’s be honest, we’re not designers, right? So we head over to Pinterest and search for “band website”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqut5k8jwa1f2ko77trwt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqut5k8jwa1f2ko77trwt.png" alt="band website pinterest reference" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I literally grabbed the first one, because we don’t need any special functionality from the design. The AI will take care of that for us. And honestly, I chose this one because it’s Joy Division. Enough said.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend in a minute
&lt;/h2&gt;

&lt;p&gt;Next, let’s go to &lt;a href="//www.skapi.com"&gt;Skapi&lt;/a&gt;. Create an account and start a free trial service. If you need help, you can find the full walkthrough &lt;a href="https://dev.to/skapi_api/no-more-backend-setup-launch-your-app-with-skapi-in-minutes-b85"&gt;here&lt;/a&gt;, but honestly, it’s straightforward even without instructions.&lt;/p&gt;

&lt;p&gt;After clicking “Create,” you’ll land on your service page. That’s where you’ll see your backend credentials. &lt;/p&gt;

&lt;p&gt;Download SKAPI.md file and copy the prompt. It already contains your credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F904z6ughf3tt26rxokj4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F904z6ughf3tt26rxokj4.png" alt="skapi credentials easy backend" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the prompt to AI
&lt;/h2&gt;

&lt;p&gt;Now let’s jump into Lovable. That’s our tool of choice for this project, because it lets us visually tweak sections of the site and then download the generated code.&lt;/p&gt;

&lt;p&gt;Here’s the prompt I’m using, and when sending it, don’t forget to attach SKAPI.md file and the reference picture:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use the file "SKAPI.md" as a system prompt. &lt;br&gt;
My Skapi service ID is: [YOUR CREDENTIALS HERE].&lt;br&gt;
Build me a website for Joy Division. A PNG reference is attached.&lt;br&gt;
Adapt it into an online pre-sale platform for a concert tour, where users can subscribe to a newsletter and join a waiting list to be the first to know when tickets go on sale and get early access to buy them. Send users a confirmation email using Skapi’s newsletter feature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsl5n10eq2uimr9l8fek8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsl5n10eq2uimr9l8fek8.png" alt="AI website generator" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s the functionality Lovable generated from my prompt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Newsletter subscription via Skapi — visitors can enter their email and instantly join the waitlist&lt;/li&gt;
&lt;li&gt;Email confirmation flow — users receive a confirmation email and must verify their address&lt;/li&gt;
&lt;li&gt;Success handling — automatic redirect after successful verification&lt;/li&gt;
&lt;li&gt;Tour information — clean layout with stats and pre-sale details&lt;/li&gt;
&lt;li&gt;Fully responsive design&lt;/li&gt;
&lt;li&gt;Skapi initialized in index.html using my service ID &lt;/li&gt;
&lt;li&gt;Uses subscribeNewsletter() with group: 'public' for public newsletter subscriptions&lt;/li&gt;
&lt;li&gt;Automatic confirmation email sent through Skapi&lt;/li&gt;
&lt;li&gt;Redirect URL for after the user verifies their email&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing ready website
&lt;/h2&gt;

&lt;p&gt;Now let’s test it. We can do everything directly from Lovable’s preview.&lt;br&gt;
And yes, it works. I entered my email and immediately received the verification link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhspeofiopelgw56fjikv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhspeofiopelgw56fjikv.png" alt="testing ready website" width="800" height="402"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtgn2oacigxrnx87nh1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtgn2oacigxrnx87nh1u.png" alt="email confirmation" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customize your email&lt;/strong&gt;&lt;br&gt;
You can customize all automated emails directly in your Skapi My Services Dashboard.&lt;/p&gt;

&lt;p&gt;Here’s a quick DIY walkthrough:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the Automated Emails section.&lt;/li&gt;
&lt;li&gt;Create a new entry, give it a name, and register it.&lt;/li&gt;
&lt;li&gt;Copy the generated email address (the one highlighted in red).&lt;/li&gt;
&lt;li&gt;Open your regular email provider - Gmail, Outlook, whatever you use.&lt;/li&gt;
&lt;li&gt;Write the email you want your users to receive and send it to that copied address.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That email becomes your template for the signup confirmation message. From now on, users will receive your version instead of the default one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frbzr1qem5wb9a98wy0et.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frbzr1qem5wb9a98wy0et.png" alt="automated emails" width="800" height="357"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qoc2e46sp83g9qrkwqu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qoc2e46sp83g9qrkwqu.png" alt="automated email send" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your website
&lt;/h2&gt;

&lt;p&gt;Now, back to our Joy Division website.&lt;/p&gt;

&lt;p&gt;The final step is deployment — and here’s the good news:&lt;br&gt;
You can handle everything right inside Skapi, without hunting for third-party hosting or paying extra.&lt;/p&gt;

&lt;p&gt;Here’s how:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to Skapi and open File Hosting from the left dashboard.&lt;/li&gt;
&lt;li&gt;Create a new domain by choosing a name and clicking Register.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapjr48z2joxw6y90xeh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapjr48z2joxw6y90xeh4.png" alt="skapi file hosting feature" width="800" height="349"&gt;&lt;/a&gt;&lt;br&gt;
Next, head over to Lovable:&lt;/p&gt;

&lt;p&gt;Click the GitHub button and connect your GitHub account to create a repository.&lt;br&gt;
If it’s your first time, just follow Lovable’s guided steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97h7xnk7qmo5gtaoptvz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F97h7xnk7qmo5gtaoptvz.png" alt="link to github" width="800" height="291"&gt;&lt;/a&gt;&lt;br&gt;
Once the repo is generated, Lovable will give you a link, open it on GitHub and download the code as a ZIP file.&lt;/p&gt;

&lt;p&gt;Inside the ZIP, locate your dist folder (you’ll need to build it through your Terminal) - that’s the production-ready build.&lt;/p&gt;

&lt;p&gt;Go back to Skapi → File Hosting and upload all the files from the dist folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1gw43ri9fgbm57v39qt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1gw43ri9fgbm57v39qt.png" alt="skapi file hosting" width="800" height="345"&gt;&lt;/a&gt;&lt;br&gt;
And that’s it - your Joy Division pre-sale site is live! You can check our version here: &lt;a href="https://joydivision.skapi.com/" rel="noopener noreferrer"&gt;https://joydivision.skapi.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s honestly incredibly fast and simple. And if you want to go further, you absolutely can. You can ask the AI to build a full user dashboard showing upcoming tour details, their position in the waiting list, login/signup flows, and even an admin panel with restricted access so you can manage users and send notifications. Or you can push the design in a more complex or artistic direction. &lt;/p&gt;

&lt;p&gt;With this setup, pretty much anything is possible.&lt;/p&gt;

&lt;p&gt;You can also skip Lovable entirely and ask ChatGPT to generate the HTML code for you. But keep in mind that if you need visual changes later, raw code isn’t nearly as flexible if you don’t know how to code. Most AI models will regenerate the entire page, which often breaks everything you already built. That’s why a visual AI website builder is usually the more practical choice.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this walkthrough. If you have any questions or need help while following the guide, let us know in the comments or send us an email at &lt;a href="mailto:support@broadwayinc.com"&gt;support@broadwayinc.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And remember: Skapi gives you a single, unified backend you can reuse across every AI-built project.&lt;/p&gt;

&lt;p&gt;Whether you build in Lovable, Bolt, Claude, Cursor, or raw HTML, your backend remains stable, portable, and easy to understand.&lt;/p&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How Skapi Helped Me Finally Get a Raise with Just 15 Minutes of Code</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Fri, 14 Nov 2025 10:10:39 +0000</pubDate>
      <link>https://dev.to/skapi_api/how-skapi-helped-me-finally-get-a-raise-with-just-15-minutes-of-code-58ab</link>
      <guid>https://dev.to/skapi_api/how-skapi-helped-me-finally-get-a-raise-with-just-15-minutes-of-code-58ab</guid>
      <description>&lt;p&gt;A few weeks ago, my boss asked me to go through a huge stack of resumes to help shortlist candidates. It sounded simple until I realized just how many there were. I spent hours reading through pages, trying to pick out key skills and relevant experience. It was exhausting.&lt;/p&gt;

&lt;p&gt;So instead of getting buried in paperwork, I did what developers do best, I automated the process.&lt;/p&gt;

&lt;p&gt;The goal was to build a &lt;strong&gt;resume summarizer app&lt;/strong&gt; that could generate short, clear summaries using AI. Since I'm a &lt;a href="https://www.skapi.com/" rel="noopener noreferrer"&gt;Skapi&lt;/a&gt; user, a serverless backend that makes things ridiculously simple, I figured it was the fastest way to pull this off.&lt;/p&gt;

&lt;p&gt;In less than 15 minutes, I had a fully functional web app running on &lt;strong&gt;plain HTML and JavaScript&lt;/strong&gt;, powered by Skapi for authentication and data handling, no server setup, no database stress.&lt;/p&gt;

&lt;p&gt;The result? A clean AI-powered tool that saved hours of manual work and made me look like a productivity hero. My boss was impressed, and let’s just say it paid off in the best way possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Frontend] ←→ [Skapi] ←→ [Google Auth + Gemini API]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr03g69slsp2bz3apik2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr03g69slsp2bz3apik2m.png" alt="CV resume summarizer app UI" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s what we’ll cover in this guide to achieve the screenshot above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Login&lt;/strong&gt; – implemented with Skapi’s OpenID (no backend code required).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini AI Integration&lt;/strong&gt; – to generate smart, professional resume summaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skapi Records&lt;/strong&gt; – to securely save and fetch summaries for each logged-in user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the best part? It all runs entirely on the frontend &lt;strong&gt;no backend servers required&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By the end of this tutorial, you’ll see exactly how our “15-minute experiment” ended up impressing the boss enough to get me a raise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Implementation: The Magic Pieces
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Skapi Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Head to Skapi.com and create an account.&lt;br&gt;
2.Once logged in, create a new service in your Dashboard.&lt;br&gt;
3.You’ll find there your unique Service ID&lt;br&gt;
4.Add it to your frontend script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Skapi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SERVICE_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fug8hltzwj0puevtq6akb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fug8hltzwj0puevtq6akb.jpg" alt="for frontend developers" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These IDs connect your frontend directly to Skapi’s secure backend with no custom server setup required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Configure Secrets in Skapi&lt;/strong&gt;&lt;br&gt;
In your Skapi Dashboard → Client Secrets, add two secrets:&lt;/p&gt;

&lt;p&gt;1.Google OAuth Client Secret&lt;/p&gt;

&lt;p&gt;Name: &lt;code&gt;ggltoken&lt;/code&gt;&lt;br&gt;
Value: Your Google OAuth Client Secret from Google Cloud Console&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fciomwv8takysceggzpm5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fciomwv8takysceggzpm5.png" alt="google 0auth client secret" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2.Gemini API Key&lt;/p&gt;

&lt;p&gt;Name: &lt;code&gt;GEMINI_API_KEY&lt;/code&gt;&lt;br&gt;
Value: Your Gemini API key from &lt;a href="https://aistudio.google.com/app/apikey" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgpwx59uv6eh84izxzqid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgpwx59uv6eh84izxzqid.png" alt="gemini api key" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Skapi securely stores these secrets and injects them automatically when using &lt;code&gt;clientSecretRequest()&lt;/code&gt; — meaning your frontend never exposes sensitive data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bdlpxyofyiy0253vnru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7bdlpxyofyiy0253vnru.png" alt="client secret key" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Set Up Google OAuth&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to the Google Cloud Console:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to APIs &amp;amp; Services → Credentials&lt;/li&gt;
&lt;li&gt;Create a new OAuth 2.0 Client ID&lt;/li&gt;
&lt;li&gt;Set Application Type to Web Application&lt;/li&gt;
&lt;li&gt;Add your redirect URL (e.g., &lt;code&gt;http://localhost:5500/&lt;/code&gt;) under Authorized redirect URIs&lt;/li&gt;
&lt;li&gt;Copy your Client ID and Client Secret&lt;/li&gt;
&lt;li&gt;Add your &lt;code&gt;GOOGLE_CLIENT_ID&lt;/code&gt; to the script:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GOOGLE_CLIENT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_SECRET_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ggltoken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OPENID_LOGGER_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;google&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;REDIRECT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5500/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhikadb4y7v8z971et3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhikadb4y7v8z971et3o.png" alt="google cloud" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Google Login Flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Integrating Google OAuth&lt;/p&gt;

&lt;p&gt;A. Redirect User to Google&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;googleLogin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://accounts.google.com/o/oauth2/v2/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;?client_id=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;redirect_uri=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;response_type=code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;scope=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openid email profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;prompt=consent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;access_type=offline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;state=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;B. Handle the Redirect and Log In Securely&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleRedirect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Exchange auth code for tokens securely via Skapi&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenResp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clientSecretRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;clientSecretName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_SECRET_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://oauth2.googleapis.com/token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$CLIENT_SECRET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Skapi injects this securely&lt;/span&gt;
      &lt;span class="na"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REDIRECT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization_code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokenResp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Use OpenID login with the returned token&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openIdLogin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OPENID_LOGGER_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Logged in user:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
  Refer to this for more insights &lt;a href="https://docs.skapi.com/authentication/openid-login.html#google-oauth-example" rel="noopener noreferrer"&gt;Google-OAuth-Example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Skapi’s &lt;code&gt;clientSecretRequest()&lt;/code&gt; handles the token exchange securely — the client secret is never exposed in your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Summarize Resumes with Gemini&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the user logs in, they can paste their resume text and summarize it using Gemini:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;summarizeBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resumeText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please paste your résumé text first.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clientSecretRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;clientSecretName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$CLIENT_SECRET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Summarize this resume in 5 bullet points:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And retrieving past summaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecords&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resume_summaries&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;access_group: "private"&lt;/code&gt; setting ensures that only the logged-in user can access their own data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7: Putting It All Together&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s the simplified flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User clicks Login with Google&lt;/li&gt;
&lt;li&gt;Google redirects back → Skapi securely exchanges tokens&lt;/li&gt;
&lt;li&gt;User pastes resume text&lt;/li&gt;
&lt;li&gt;Gemini API summarizes it&lt;/li&gt;
&lt;li&gt;Summary is saved securely to the user’s private database&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What You’ve Built
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Secure Google Authentication using OAuth and Skapi&lt;/li&gt;
&lt;li&gt;AI-Powered Summarization with Gemini API&lt;/li&gt;
&lt;li&gt;Private Data Storage per user in Skapi’s database&lt;/li&gt;
&lt;li&gt;100% serverless and frontend-only implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This combination lets you build production-ready apps without deploying your own backend.&lt;/p&gt;

&lt;p&gt;You can find the whole project in our &lt;a href="https://github.com/broadwayinc/demo_resume_summary" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>When Telegram Cocoon Goes Live: The Future of the AI Internet</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Fri, 31 Oct 2025 09:39:35 +0000</pubDate>
      <link>https://dev.to/skapi_api/when-telegram-cocoon-goes-live-the-future-of-the-ai-internet-2e67</link>
      <guid>https://dev.to/skapi_api/when-telegram-cocoon-goes-live-the-future-of-the-ai-internet-2e67</guid>
      <description>&lt;p&gt;At &lt;strong&gt;Blockchain Life 2025&lt;/strong&gt; in Dubai, &lt;strong&gt;Telegram CEO Pavel Durov&lt;/strong&gt; unveiled &lt;strong&gt;Cocoon (Confidential Compute Open Network)&lt;/strong&gt; — a decentralized AI compute network built on &lt;strong&gt;The Open Network (TON)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cocoon will allow GPU owners around the world to contribute computing power for AI inference and get rewarded in &lt;strong&gt;Toncoin (TON)&lt;/strong&gt;. Telegram itself will become Cocoon’s first large-scale user, integrating this technology into its &lt;strong&gt;Mini Apps and AI tools&lt;/strong&gt; for its &lt;strong&gt;billion-plus users&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cocoon in a Nutshell
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compute&lt;/strong&gt;: Decentralized AI tasks running securely across a distributed network of GPUs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Incentives&lt;/strong&gt;: GPU miners are rewarded in TON for powering these computations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Privacy&lt;/strong&gt;: Data remains encrypted end-to-end — even from the machines processing it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Goal&lt;/strong&gt;: To challenge centralized AI infrastructure monopolies like AWS and Azure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cocoon represents a privacy-first approach to AI infrastructure, aligning with Telegram and TON’s shared vision of &lt;strong&gt;user-owned digital freedom&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Problem of Cocoon
&lt;/h2&gt;

&lt;p&gt;While Cocoon decentralizes &lt;strong&gt;AI compute&lt;/strong&gt;, building real applications requires much more than inference.&lt;/p&gt;

&lt;p&gt;Developers still need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle user authentication&lt;/li&gt;
&lt;li&gt;Manage data persistence&lt;/li&gt;
&lt;li&gt;Store and serve user-generated content&lt;/li&gt;
&lt;li&gt;Host frontend interfaces&lt;/li&gt;
&lt;li&gt;Securely connect all these components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cocoon offers a distributed engine for AI workloads — but it doesn’t solve the &lt;strong&gt;data and logic layer&lt;/strong&gt; that connects these models to real users and applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Missing Layer of Decentralized Infrastructure
&lt;/h2&gt;

&lt;p&gt;Telegram will be Cocoon’s first client, embedding it into its vast ecosystem of Mini Apps.&lt;/p&gt;

&lt;p&gt;But as the network expands, developers and businesses will be the next wave of adopters.&lt;/p&gt;

&lt;p&gt;To participate in that ecosystem — to build AI-powered apps that leverage Cocoon’s decentralized compute — you’ll need a backend that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages your users securely&lt;/li&gt;
&lt;li&gt;Stores their data without central control&lt;/li&gt;
&lt;li&gt;Authenticates AI access and app permissions&lt;/li&gt;
&lt;li&gt;Scales automatically as your app grows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s exactly what &lt;a href="https://www.skapi.com/" rel="noopener noreferrer"&gt;Skapi&lt;/a&gt; provides. &lt;strong&gt;Skapi&lt;/strong&gt; provides a &lt;strong&gt;serverless backend for web apps&lt;/strong&gt;, purpose-built for the privacy-centric future Cocoon is enabling.&lt;/p&gt;

&lt;p&gt;Developers using Skapi get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authentication &amp;amp; identity&lt;/strong&gt;: Secure user accounts without traditional servers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Databases&lt;/strong&gt;: Structured data storage with private, encrypted access&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File hosting&lt;/strong&gt;: For AI models, assets, and user uploads&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero DevOps&lt;/strong&gt;: No configuration, no server maintenance, no central authority&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skapi already supports a &lt;strong&gt;decentralized approach to data&lt;/strong&gt;: users own their data, and developers never need to expose it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A New Web Stack for Decentralized AI Apps
&lt;/h2&gt;

&lt;p&gt;It’s important to note that &lt;strong&gt;Cocoon doesn’t store user data&lt;/strong&gt; — it only processes it securely.&lt;/p&gt;

&lt;p&gt;That means your application still needs a trusted layer to &lt;strong&gt;store, recall, and protect user information&lt;/strong&gt; before and after AI computation happens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyz5gcht159di1ye3q9k3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyz5gcht159di1ye3q9k3.png" alt="New Web Stack for Decentralized AI Apps" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This architecture keeps every layer decentralized, private, and user-owned.&lt;/p&gt;

&lt;p&gt;This is what we can start calling &lt;strong&gt;Private AI-as-a-Service (PAaaS)&lt;/strong&gt; — a model where both compute and storage are user-owned and secured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developers Build AI Web3 Applications
&lt;/h2&gt;

&lt;p&gt;With Cocoon and Skapi together, developers can finally build &lt;strong&gt;truly independent applications&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI-powered bots&lt;/strong&gt; that process private data without central servers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mini Apps&lt;/strong&gt; that run entirely on decentralized compute and storage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Full-stack businesses&lt;/strong&gt; where both compute and data belong to the users&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of Skapi as your bridge into Cocoon’s ecosystem — the part that allows you to manage your users, data, and authentication &lt;strong&gt;without betraying the principles of decentralization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because if your AI runs on a decentralized GPU network, your users still log in through a centralized database, you’re &lt;strong&gt;only halfway free&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s where Skapi bridges the gap, enabling developers to build full-stack decentralized apps that align perfectly with Cocoon’s vision:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An internet where AI is private, infrastructure is open, and ownership belongs to the user.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

&lt;p&gt;Let us know in the comments what's your vision of future Internet!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>cloud</category>
    </item>
    <item>
      <title>The Meme Game That Changed How We Build Secure APIs — Skapi’s Story</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Fri, 17 Oct 2025 09:54:16 +0000</pubDate>
      <link>https://dev.to/skapi_api/the-cat-that-changed-api-rules-164p</link>
      <guid>https://dev.to/skapi_api/the-cat-that-changed-api-rules-164p</guid>
      <description>&lt;h2&gt;
  
  
  How a meme app improved Skapi’s backend logic (and made our cat very happy)
&lt;/h2&gt;

&lt;p&gt;It started as a joke.&lt;/p&gt;

&lt;p&gt;We wanted to make a &lt;a href="https://shouldifeedthecat.skapi.com/" rel="noopener noreferrer"&gt;tiny web app&lt;/a&gt; called “&lt;strong&gt;Should I Feed the Cat&lt;/strong&gt;?” - a pixel-art voting game where anyone could click &lt;strong&gt;YES&lt;/strong&gt; or &lt;strong&gt;NO&lt;/strong&gt;, watch the scores rise in real time, and decide the fate of a digital cat.&lt;/p&gt;

&lt;p&gt;But that dumb little cat ended up exposing something much bigger: a flaw in how we handled &lt;strong&gt;public data submission&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The meme cat web app in question:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g4bk6biw494cafgv05v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g4bk6biw494cafgv05v.png" alt="meme cat" width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the process, this little web application changed how &lt;strong&gt;Skapi&lt;/strong&gt;, our secure backend platform, works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Login Walls vs. Public Data
&lt;/h2&gt;

&lt;p&gt;Originally, Skapi was designed with strict security in mind.&lt;/p&gt;

&lt;p&gt;Everything a user sent to the database had to come from a logged-in session. That made sense for apps dealing with sensitive or user-specific data, things like notes, messages, or private uploads.&lt;/p&gt;

&lt;p&gt;But for our cat game, this was absurd.&lt;/p&gt;

&lt;p&gt;Did we really need to force people to &lt;strong&gt;sign up and log in&lt;/strong&gt; just to click “YES, feed the cat”?&lt;/p&gt;

&lt;p&gt;The cat didn’t care who you were.&lt;br&gt;
It just wanted food.&lt;/p&gt;

&lt;p&gt;Yet our system said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“postRecord(): Login is required.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was the moment we realized something was off. The world needed a way to collect &lt;strong&gt;anonymous, public data&lt;/strong&gt; safely.&lt;/p&gt;
&lt;h2&gt;
  
  
  When Security is a Problem
&lt;/h2&gt;

&lt;p&gt;“Should I Feed the Cat?” was supposed to be a lighthearted test of Skapi’s frontend integration.&lt;/p&gt;

&lt;p&gt;We added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pixel-style cats (waiting, happy, dead)&lt;/li&gt;
&lt;li&gt;Eye blinks and tear animations&lt;/li&gt;
&lt;li&gt;Confetti explosions for 10,000 “YES” votes&lt;/li&gt;
&lt;li&gt;Thunderstorms if the “NO” side wins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each visitor could spam votes as much as they wanted.&lt;br&gt;
It was chaotic, hilarious, and strangely addictive.&lt;/p&gt;

&lt;p&gt;But when we deployed it, reality hit:&lt;br&gt;
No one could vote unless they logged in.&lt;/p&gt;

&lt;p&gt;The backend was doing its job — protecting data integrity — but in this case, &lt;strong&gt;the protection was the problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We wanted people to interact instantly and just have fun without login.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Breakthrough: Public postRecord()
&lt;/h2&gt;

&lt;p&gt;That’s when we realized something powerful.&lt;/p&gt;

&lt;p&gt;We could safely open part of Skapi’s data system to &lt;strong&gt;public anonymous writes&lt;/strong&gt;, without exposing user data or compromising backend security.&lt;/p&gt;

&lt;p&gt;How?&lt;/p&gt;

&lt;p&gt;By allowing the &lt;code&gt;postRecord()&lt;/code&gt; method to accept submissions from unauthenticated users when writing to public tables.&lt;/p&gt;

&lt;p&gt;Here’s how it looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postRecord&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;nickname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;choice&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat_votes_open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`choice.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s easy to just let anonymous users post data to a backend, but doing it safely was the hard part.&lt;/p&gt;

&lt;p&gt;We needed a way to ensure that anonymous users couldn’t access restricted tables or modify anyone else’s data.&lt;/p&gt;

&lt;p&gt;Normally, Skapi enforces permissions using profile information from a user’s JWT token. But since anonymous users don’t have one, Skapi now generates a randomized, temporary security hash derived from each client’s public address.&lt;/p&gt;

&lt;p&gt;Behind the scenes, Skapi also fully separates &lt;strong&gt;public tables&lt;/strong&gt; from &lt;strong&gt;restricted tables&lt;/strong&gt;, ensuring that anonymous submissions can only write new records but never edits or deletes existing ones. Each anonymous client is tracked as its own isolated device identity, allowing open participation without compromising data integrity or security.&lt;/p&gt;

&lt;p&gt;That’s it. No login. No auth token. The cat finally got fed.&lt;/p&gt;

&lt;p&gt;Under the hood, Skapi still handles all security:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public tables are isolated from private ones.&lt;/li&gt;
&lt;li&gt;No user data is exposed.&lt;/li&gt;
&lt;li&gt;The backend validates every request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rate limits and data shape protections still apply.&lt;br&gt;
In short — &lt;strong&gt;anyone can submit data, but no one can break the system.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Technical Key of Viral Web Apps
&lt;/h2&gt;

&lt;p&gt;This isn’t just about a cat. It’s about lowering friction in web apps that rely on &lt;em&gt;public engagement&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Think:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Polls &amp;amp; surveys&lt;/li&gt;
&lt;li&gt;Comment walls&lt;/li&gt;
&lt;li&gt;Feedback widgets&lt;/li&gt;
&lt;li&gt;Micro games&lt;/li&gt;
&lt;li&gt;Anonymous reactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For all these, forcing logins just kills the vibe.&lt;br&gt;
But letting the world interact freely, while keeping the backend secure, is how you make &lt;strong&gt;lightweight, viral apps&lt;/strong&gt; that spread fast.&lt;/p&gt;

&lt;p&gt;With this Skapi update, developers can now collect &lt;strong&gt;public, anonymous data safely.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s something few backends do right.&lt;/p&gt;
&lt;h2&gt;
  
  
  Secure vs. Accessible Web Application
&lt;/h2&gt;

&lt;p&gt;Our little meme experiment revealed a gap between “secure” and “accessible.” And fixing it made Skapi better for everyone.&lt;/p&gt;

&lt;p&gt;Now, anyone can use &lt;code&gt;skapi.postRecord()&lt;/code&gt; to collect open data, whether it’s a public poll, an art project, or a crowdsourced idea board.&lt;/p&gt;

&lt;p&gt;We built a cat game. The cat changed our API.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;If you want to play (or feed the cat), you can check it out here: &lt;a href="https://shouldifeedthecat.skapi.com/" rel="noopener noreferrer"&gt;https://shouldifeedthecat.skapi.com/&lt;/a&gt;, or build your own version in minutes.&lt;/p&gt;

&lt;p&gt;Here’s the minimal setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.jsdelivr.net/npm/skapi-js@latest/dist/skapi.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Skapi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SERVICE_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postRecord&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;choice&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public_votes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`choice.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s how a real backend logic with zero setup works.&lt;/p&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>api</category>
      <category>security</category>
    </item>
    <item>
      <title>Skapi vs Firebase vs Supabase: Choosing the Best Backend for Performance and Scalability</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Thu, 02 Oct 2025 10:33:15 +0000</pubDate>
      <link>https://dev.to/skapi_api/skapi-vs-firebase-vs-supabase-choosing-the-best-backend-for-performance-and-scalability-27e9</link>
      <guid>https://dev.to/skapi_api/skapi-vs-firebase-vs-supabase-choosing-the-best-backend-for-performance-and-scalability-27e9</guid>
      <description>&lt;p&gt;Choosing the right backend service is crucial for the performance, scalability, and user experience of your application. From query response times to concurrent user handling and file upload speeds, your backend directly impacts how smooth, fast, and reliable your app feels.&lt;/p&gt;

&lt;p&gt;Pick the wrong backend, and you risk getting locked into infrastructure that slows down your app, limits scalability, and frustrates users with lag and downtime. Pick the right one, and you’re set up for growth and speed from day one.&lt;/p&gt;

&lt;p&gt;While Firebase and Supabase are popular backend-as-a-service (BaaS) platforms, their added complexity can introduce performance overhead. Skapi takes a different approach: offering speed and scalability out of the box. &lt;/p&gt;

&lt;p&gt;In this article, we’ll compare Firebase, Supabase, and Skapi across three key backend performance metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query Response Times (basic and complex)&lt;/li&gt;
&lt;li&gt;Concurrent User Handling (scalability under load)&lt;/li&gt;
&lt;li&gt;File Upload Speeds (media-heavy app performance)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. Query Response Times
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Basic Queries:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Basic query response time is the foundation of backend performance. If it lags, your whole app feels broken, from dashboards to search results to profile loads.&lt;/p&gt;

&lt;p&gt;Here’s the query we will be requesting to the backend:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skapi:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecords&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;performance_test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fetch_limit&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Supabase:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;performance_test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch_limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Firebase:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;performance_test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will do simple data fetching query testing with various ranges of fetch limits.&lt;/p&gt;

&lt;p&gt;In benchmark tests, Skapi consistently outperforms Firebase and Supabase in basic query speed, making apps feel instantly responsive without extra optimization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrn8xvkq0cvcyvkkaaed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrn8xvkq0cvcyvkkaaed.png" alt="Firebase Supabase Skapi benchmark tests" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complex Queries:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Complex queries such as joins, sorts, or aggregations, push systems harder. Firebase and Supabase tend to slow down as complexity grows, while Skapi maintains low latency even under demanding query loads.&lt;/p&gt;

&lt;p&gt;Each service uses a different underlying database model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Skapi&lt;/strong&gt;: NoSQL with auto-indexing and client-defined access control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase&lt;/strong&gt;: NoSQL document store (Firestore) with structured indexes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt;: SQL/Postgres with schema-driven indexes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To compare, here are equivalent examples of range queries:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skapi:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecords&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;performance_test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test_index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Supabase:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;supabase&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;performance_test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-&amp;gt;test_index_value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-&amp;gt;test_index_value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Firebase:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;performance_test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test_index_value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test_index_value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notes on Indexing and Joins&lt;/strong&gt;&lt;br&gt;
Index condition queries rely on conditional lookups using comparison operators &lt;code&gt;(&amp;gt;, &amp;lt;, &amp;gt;=, &amp;lt;=)&lt;/code&gt; on indexed fields.&lt;/p&gt;

&lt;p&gt;For more complex operations such as joins, sorts, or aggregations, the approach differs by platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skapi&lt;/strong&gt;: Supports indexed conditional lookups directly, but for multi-table relationships or advanced aggregations, data is typically fetched in ranges and then joined or aggregated on the client.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Firebase (Firestore)&lt;/strong&gt;: Works similarly - queries can filter indexed fields efficiently, but there is no native join support. Developers usually fetch multiple datasets and merge them client-side. Composite indexes must be explicitly created for more complex queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supabase (Postgres)&lt;/strong&gt;: Can handle joins, sorts, and aggregations directly in SQL, but these operations are heavier on the backend. While powerful, they often introduce more latency compared to client-side merges, especially under load.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4o84pp7dyz3xn2kp68qz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4o84pp7dyz3xn2kp68qz.png" alt="Indexing and joins query" width="800" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Skapi ensures your app’s navigation, filtering, and data-heavy features remain fast and smooth, no matter the complexity.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Concurrent User Handling
&lt;/h2&gt;

&lt;p&gt;Scalability isn’t just about spinning up more servers, it’s about maintaining performance under heavy load. Some backends choke as traffic scales.&lt;/p&gt;

&lt;p&gt;In stress tests, Skapi’s backend architecture holds steady, delivering fast, consistent response times even with thousands of concurrent users. This makes it ideal for apps expecting growth, viral traffic spikes, or global audiences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples of concurrent test:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Sends multiple requests simultaneously&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;concurrentCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecords&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;performance_test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// All execute at once&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F211zv6radv7yp2syjwko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F211zv6radv7yp2syjwko.png" alt="concurrent user handling" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Skapi, you get true horizontal scalability, meaning your app grows seamlessly without compromising speed or user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes on Concurrency&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skapi&lt;/strong&gt;: Designed with serverless elasticity; concurrency scaling is handled automatically by the platform.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt;: Concurrency depends on underlying Postgres + connection pooling; under extreme load, queries may queue or slow down.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt;: Firestore handles a high degree of concurrent reads and writes, but performance can degrade if many clients hit the same document paths, since hot-spotting leads to throttling.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. File Upload Speeds
&lt;/h2&gt;

&lt;p&gt;For apps handling images, video, or large files, upload performance is critical. No user wants to stare at a stuck progress bar.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firebase and Supabase can slow down significantly on large file uploads.&lt;/li&gt;
&lt;li&gt;Skapi maintains consistently fast uploads across small and large files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsaci9ofbszu5n2ax0fo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsaci9ofbszu5n2ax0fo.png" alt="file upload speed" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In fact, the performance gap widens as file sizes increase, making Skapi the clear winner for media-heavy apps, SaaS platforms, and social apps.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the Tests Were Conducted
&lt;/h2&gt;

&lt;p&gt;To ensure a fair and transparent comparison, each test followed structured benchmarks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Basic Query Test&lt;/strong&gt;: Measured how quickly backends respond to simple database queries (profile loads, product lists, or recent posts). Average, min, and max response times were recorded, with success rates checked for 100% delivery.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complex Query Test&lt;/strong&gt;: Ran 16 variations across 8 categories, including index lookups, range queries, tag searches, sorting, aggregations, and multi-filters. Data sets included realistic fields like price, rating, category, and tags.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concurrent User Test&lt;/strong&gt;: Simulated loads from 5 to 100,000 simultaneous users, measuring how response times and success rates held up under spikes (such as flash sales, viral content, or product launches).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File Upload Test&lt;/strong&gt;: Uploaded files from 1KB to 1GB, tracking average speeds and time-to-completion. These mirrored real-world use cases such as profile images, PDFs, audio, and full HD videos.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All tests were repeated multiple times under identical conditions to rule out anomalies. Data sets were auto-cleaned after runs to ensure fair results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Skapi Wins on Performance
&lt;/h2&gt;

&lt;p&gt;Across all three categories  - query response time, concurrent user handling, and file uploads, Skapi outperforms Firebase and Supabase.&lt;/p&gt;

&lt;p&gt;The key technical advantages that set Skapi apart are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fully serverless infrastructure&lt;/strong&gt;: elastic scaling without servers to deploy or manage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;NoSQL with unique indexing&lt;/strong&gt;: optimized for high-speed lookups and range queries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Direct client-to-backend access&lt;/strong&gt;: no middle-layer bottlenecks slowing things down&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By choosing Skapi, you’re building on a backend that guarantees speed, scalability, and responsiveness from day one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts: Skapi vs Firebase vs Supabase
&lt;/h2&gt;

&lt;p&gt;If your app’s success depends on speed, scalability, and user experience, Skapi is the backend built for you. While Firebase and Supabase are solid options, their overhead can slow you down as your project scales.&lt;/p&gt;

&lt;p&gt;Skapi delivers performance superiority across the board, ensuring your app stays fast, reliable, and scalable, whether you’re building a social media platform, SaaS product, or media-heavy mobile app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short&lt;/strong&gt;: With Skapi, there are no delays, no bottlenecks, and no compromises, just the backend performance your project deserves.&lt;/p&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

</description>
      <category>database</category>
      <category>testing</category>
      <category>backend</category>
      <category>serverless</category>
    </item>
    <item>
      <title>How to Create a Video Streaming Platform with HTML, CSS, JS, and Skapi</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Fri, 19 Sep 2025 09:02:16 +0000</pubDate>
      <link>https://dev.to/skapi_api/how-to-create-a-video-streaming-platform-with-html-css-js-and-skapi-2f23</link>
      <guid>https://dev.to/skapi_api/how-to-create-a-video-streaming-platform-with-html-css-js-and-skapi-2f23</guid>
      <description>&lt;p&gt;These days, we all stream everything: movies, shows, random videos we find online. But the big streaming platforms often come with strict rules, changing algorithms, and sometimes even censorship.&lt;/p&gt;

&lt;p&gt;For small creators, that can be exhausting. So many creators are taking a different path: building their own little streaming spaces. Smaller, more personal platforms where they can share what they want, connect directly with their audience, and stay in control over their content.&lt;/p&gt;

&lt;p&gt;Building your own platform can be tough, especially when it comes to setting up the backend and database. In this guide, we’ll explain step-by-step so even beginners can follow along and create a working video-on-demand platform in a single afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Skapi
&lt;/h2&gt;

&lt;p&gt;Skapi is here to help you do it, taking away the stress of developing a web infrastructure and allowing you to focus on giving your viewers the best experience. Thanks to Skapi, a modern backend-as-a-service (BaaS), you can set up user accounts, video uploads, and content delivery using just HTML, CSS, and JavaScript. No servers, no complicated set up, just a few lines of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Connecting your project with Skapi is a very easy process. You can follow a step-by-step guide on how to do it in our &lt;a href="https://dev.to/skapi_api/no-more-backend-setup-launch-your-app-with-skapi-in-minutes-b85"&gt;No More Backend Setup: Launch Your App with Skapi in Minutes&lt;/a&gt; article. &lt;/p&gt;

&lt;h2&gt;
  
  
  Adding User Authentication
&lt;/h2&gt;

&lt;p&gt;Every video streaming platform needs user accounts. Skapi makes this simple with built-in signup and login methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signup Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Password must be between 6 and 60 characters.&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User's name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// If set to true, users will be automatically logged in after signup.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Login Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user@email.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;welcome.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once logged in, users can upload videos or view restricted content depending on your settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading Videos
&lt;/h2&gt;

&lt;p&gt;To upload videos and any other file, you can use the method highlighted in &lt;a href="https://dev.to/skapi_api/the-easiest-way-to-add-file-upload-to-your-website-34bn"&gt;The Easiest Way to Add File Upload to Your Website&lt;/a&gt; article. But just as a quick example, here’s how to do it with a &lt;code&gt;form&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;onsubmit=&lt;/span&gt;&lt;span class="s"&gt;"skapi.postRecord(event, {
    table: {
        name: 'videos',
        access_group: 'public'
    })&amp;gt;
  &amp;lt;input name="&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Video Title"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Video Description"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"video"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On submitting the form, your video will be uploaded to Skapi via the &lt;code&gt;postRecord()&lt;/code&gt; method, making it instantly available to be watched.&lt;/p&gt;

&lt;h2&gt;
  
  
  Display Videos on Your Platform
&lt;/h2&gt;

&lt;p&gt;Fetch all uploaded videos and display them in a simple gallery with HTML5 &lt;code&gt;video&lt;/code&gt; tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"videoGallery"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadVideos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;videos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecords&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gallery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videoGallery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;gallery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;videos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div&amp;gt;
      &amp;lt;h3&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/h3&amp;gt;
      &amp;lt;video src="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" controls width="480"&amp;gt;&amp;lt;/video&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;loadVideos&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a dynamic video feed — as soon as someone uploads a video, it appears for viewers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Premium Content
&lt;/h2&gt;

&lt;p&gt;Sometimes you’ll want to control who can see your content. &lt;br&gt;
When posting any type of &lt;code&gt;record&lt;/code&gt; to Skapi, one of the config options is &lt;code&gt;access_group&lt;/code&gt;. This is used to limit the visibility of the record, and is used as such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Default: 'public'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;access_group&lt;/code&gt; config is optional and defaults to public if not specified. The possible settings are: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;number&lt;/code&gt;: Integer from 0 to 99. Compares the record access requirement to the user profile access level. Only users with access levels equal or greater than the record’s requirement may see it. User's access level can be upgraded from the "Users" page in Skapi service dashboard.&lt;br&gt;
&lt;code&gt;private&lt;/code&gt;: Only the uploader is allowed to have access to the record. The uploader can give access to others.&lt;br&gt;
&lt;code&gt;public&lt;/code&gt;: Everyone can see the record. (Equivalent to &lt;code&gt;number&lt;/code&gt;: 0)&lt;br&gt;
&lt;code&gt;authorized&lt;/code&gt;: Only people logged to the project may see the record. (Equivalent to &lt;code&gt;number&lt;/code&gt;: 1)&lt;br&gt;
&lt;code&gt;admin&lt;/code&gt;: Only the admins of your service will be allowed to view this record. (Equivalent to &lt;code&gt;number&lt;/code&gt;: 99)&lt;/p&gt;

&lt;p&gt;Want to charge for videos or keep some content exclusive?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;access_group&lt;/code&gt;: 2 to 4 for example, when uploading.&lt;/li&gt;
&lt;li&gt;Now you have tiered content, all you need to do is charge to upgrade users accounts to higher tiers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Subscriptions
&lt;/h2&gt;

&lt;p&gt;Another important aspect of streaming is being able to subscribe to your preferred content creators. Using Skapi’s subscriptions system you won’t have to worry about designing a whole subscription pipeline. It’s all ready to be used.&lt;/p&gt;

&lt;p&gt;When uploading a video, you can choose to make it available only to your subscribers, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;is_subscription_record&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will make it so that only users subscribed to the uploader will be able to see the video.&lt;br&gt;
And to subscribe is even easier, just use the &lt;code&gt;subscribe()&lt;/code&gt; method, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_id_of_user_A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you can have subscriber only content in your platform, allowing another type of exclusivity to your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it!
&lt;/h2&gt;

&lt;p&gt;As you can see, Skapi empowers you to create a streaming platform with full user authentication, tiered content and subscriptions, all without writing a single backend line of code.&lt;/p&gt;

&lt;p&gt;Simply connect your project to a Skapi service and you can start your own streaming platform today.&lt;/p&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Build a Serverless FAQ Bot with Skapi + Vanilla JS (No Backend Needed!)</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Fri, 19 Sep 2025 09:02:05 +0000</pubDate>
      <link>https://dev.to/skapi_api/build-a-serverless-faq-bot-with-skapi-vanilla-js-no-backend-needed-20cj</link>
      <guid>https://dev.to/skapi_api/build-a-serverless-faq-bot-with-skapi-vanilla-js-no-backend-needed-20cj</guid>
      <description>&lt;p&gt;I remember the first time I wanted to add a simple FAQ bot to a website. My plan was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I’ll just throw the FAQs in a database, write a quick API… how hard can it be?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Spoiler&lt;/em&gt;: it turned into a mini backend project with hosting, endpoints, and authentication before I even wrote a single line of UI code.&lt;/p&gt;

&lt;p&gt;So when I joined &lt;strong&gt;Skapi&lt;/strong&gt; — a serverless backend for frontend developers, no coders, and anyone who wants to save their time on backend — the first thing that I did, I decided to see if I could build the entire FAQ bot with just frontend code.&lt;/p&gt;

&lt;p&gt;And you totally can. In this tutorial, I’ll show you exactly how.&lt;/p&gt;

&lt;p&gt;By the end, you’ll have a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless FAQ bot running entirely in the browser&lt;/li&gt;
&lt;li&gt;Zero backend setup (Skapi handles the data for you)&lt;/li&gt;
&lt;li&gt;Frontend-only guidance using plain HTML + JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhf6waev0db1pxpuyz2m7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhf6waev0db1pxpuyz2m7.png" alt="FAQ bot" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmy9l1z9q45wh0pkinrcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmy9l1z9q45wh0pkinrcg.png" alt="FAQ bot dataset" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Skapi Is Perfect for This
&lt;/h2&gt;

&lt;p&gt;If you’ve ever spun up a backend just to store a few rows of data, you’ll appreciate this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless: No API endpoints or servers to maintain&lt;/li&gt;
&lt;li&gt;Frontend-friendly: Works with plain HTML, React, Vue, anything&lt;/li&gt;
&lt;li&gt;Data + Auth in one place: Store records, handle users, all from the browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For this project, we’ll use it to:&lt;/strong&gt;&lt;br&gt;
1.Store FAQs in a Skapi table&lt;br&gt;
2.Fetch them with &lt;code&gt;getRecords()&lt;/code&gt;&lt;br&gt;
3.Display answers based on user input&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Setting Up Skapi
&lt;/h2&gt;

&lt;p&gt;Sign up at Skapi → create a service → copy your Service ID.&lt;br&gt;
Create an index.html and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Serverless FAQ Bot&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/skapi-js@latest/dist/skapi.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Replace with your real IDs from Skapi dashboard&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Skapi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your_service_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Serverless FAQ Bot&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"questionInput"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Ask me anything..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"getAnswer()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Ask&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"answerBox"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"faqList"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Adding FAQ Data (One-Time Setup)
&lt;/h2&gt;

&lt;p&gt;Here’s the fun part: instead of writing SQL or spinning up an API, you just run this once to store FAQs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;uploadFAQs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;faqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is Skapi?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Skapi is a serverless backend for your frontend apps.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Is Skapi free?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yes! It has a free tier perfect for small projects.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Do I need a backend?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nope. Everything runs in the frontend.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;faq&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;faqs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faqs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FAQs uploaded successfully!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;uploadFAQs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s like having a mini Firebase, but simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Building the FAQ Bot Logic
&lt;/h2&gt;

&lt;p&gt;Now let’s make the bot actually answer questions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAnswer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;userQuestion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;questionInput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Fetch all FAQs from Skapi&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecords&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faqs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Naive matching: find the first question that appears in user input&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bestMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faq&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;userQuestion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;answerBox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bestMatch&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;bestMatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hmm… I don’t have an answer for that yet.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, it’s basic string matching, but it works surprisingly well for FAQs.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Showing All FAQs
&lt;/h2&gt;

&lt;p&gt;I like showing all FAQs below the search bar so users can browse manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;showFAQs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecords&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faqs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faqList&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h3&amp;gt;All FAQs:&amp;lt;/h3&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; 
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;showFAQs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. What I Learned Building This
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Skapi is stupidly simple: Adding data feels easier than copy-pasting it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend-only apps are underrated: For small tools, backend code is often overkill.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;We just built a serverless FAQ bot in under 100 lines of code — no backend, no database headaches.&lt;/p&gt;

&lt;p&gt;Skapi turned out to be the perfect tool for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple CRUD from the frontend&lt;/li&gt;
&lt;li&gt;Scales without extra work&lt;/li&gt;
&lt;li&gt;Free tier to experiment with&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Easy and fast deployment using Skapi’s&lt;/em&gt; &lt;strong&gt;File Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re a frontend dev who wants to ship full-stack features without writing backend code, Skapi is worth a look.&lt;/p&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Link Records Without SQL: Skapi Makes It Easy</title>
      <dc:creator>Skapi</dc:creator>
      <pubDate>Fri, 05 Sep 2025 08:15:27 +0000</pubDate>
      <link>https://dev.to/skapi_api/how-to-link-records-without-sql-skapi-makes-it-easy-1063</link>
      <guid>https://dev.to/skapi_api/how-to-link-records-without-sql-skapi-makes-it-easy-1063</guid>
      <description>&lt;p&gt;Possibly the most important aspect of a relational database is that a record can reference other records in the same or different tables, allowing you to build a web of structured data. But setting up your own database, wiring primary key and foreign key constraints can be a headache, especially when you want to launch your app as soon as possible.&lt;/p&gt;

&lt;p&gt;This is where Skapi can save you hours and hours of work. Using our top of the line API, you have access to a fully functional relational database. No more coding an entire database or setting a backend-server.&lt;/p&gt;

&lt;p&gt;To start using Skapi on your project right now, follow &lt;a href="https://dev.to/skapi_api/no-more-backend-setup-launch-your-app-with-skapi-in-minutes-b85"&gt;this step-by-step guide&lt;/a&gt; and be ready to use our database and much more in just 5 minutes.&lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://dev.to/skapi_api/no-more-ddl-headaches-use-skapis-zero-setup-relational-database-api-16g4"&gt;previous article&lt;/a&gt;, we gave you an overview of how to create and fetch some simple records and now we start to delve into some nuances of creating more complex data structures within Skapi.&lt;/p&gt;

&lt;p&gt;Now, to illustrate let’s simulate a social media structure with posts and comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt; 
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;referenced_record_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reference_record_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the original post is created, Skapi returns its unique &lt;code&gt;record_id&lt;/code&gt;. We’ll store this in the variable &lt;code&gt;referenced_record_id&lt;/code&gt;. That ID now acts as the anchor for any related records, such as comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;commentRecord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;good&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;commentConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;Comments&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;referenced_record_id&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commentRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;commentConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And like this, the comment is posted. As you can see, the &lt;code&gt;reference&lt;/code&gt; property inside the &lt;code&gt;config&lt;/code&gt; data is what governs the relationship between records.&lt;/p&gt;

&lt;p&gt;You can easily fetch all data referencing the original post using the &lt;code&gt;getRecords()&lt;/code&gt; method passing the &lt;code&gt;reference&lt;/code&gt; property. Just like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;skapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRecords&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Comments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;access_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;referenced_record_id&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this powerful config you can build an intricate web of data without having to write any DDL code.&lt;/p&gt;

&lt;p&gt;It’s like setting the foreign key without having to write any SQL, all is done automatically by Skapi’s top-tier backend API to deliver you high speed data transfers. You just focus on your app logic.&lt;/p&gt;

&lt;p&gt;Follow us on socials: &lt;a href="https://x.com/skapi_api" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skapi_official" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, &lt;a href="https://www.instagram.com/skapi_api" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/skapi-backend-api/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. More tutorials, code drops, and dev hacks are coming soon.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
